@aigne/core 1.14.0 → 1.16.0

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 (145) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +9 -7
  3. package/README.zh.md +9 -7
  4. package/lib/cjs/agents/agent.d.ts +129 -6
  5. package/lib/cjs/agents/agent.js +112 -20
  6. package/lib/cjs/agents/ai-agent.d.ts +3 -2
  7. package/lib/cjs/agents/ai-agent.js +12 -9
  8. package/lib/{esm/models → cjs/agents}/chat-model.d.ts +24 -13
  9. package/lib/cjs/{models → agents}/chat-model.js +48 -7
  10. package/lib/cjs/agents/guide-rail-agent.d.ts +62 -0
  11. package/lib/cjs/agents/guide-rail-agent.js +14 -0
  12. package/lib/cjs/agents/mcp-agent.js +9 -9
  13. package/lib/cjs/agents/team-agent.js +1 -1
  14. package/lib/cjs/aigne/aigne.d.ts +3 -2
  15. package/lib/cjs/aigne/aigne.js +2 -2
  16. package/lib/cjs/aigne/context.d.ts +2 -1
  17. package/lib/cjs/aigne/context.js +8 -1
  18. package/lib/cjs/index.d.ts +1 -1
  19. package/lib/cjs/index.js +1 -1
  20. package/lib/cjs/loader/agent-yaml.d.ts +1 -1
  21. package/lib/cjs/loader/index.d.ts +18 -11
  22. package/lib/cjs/loader/index.js +8 -27
  23. package/lib/cjs/memory/retriever.d.ts +2 -2
  24. package/lib/cjs/prompt/prompt-builder.d.ts +3 -3
  25. package/lib/cjs/prompt/template.d.ts +3 -3
  26. package/lib/cjs/prompt/template.js +1 -1
  27. package/lib/cjs/utils/json-schema.js +1 -1
  28. package/lib/cjs/utils/logger.d.ts +33 -8
  29. package/lib/cjs/utils/logger.js +63 -5
  30. package/lib/cjs/utils/model-utils.d.ts +1 -1
  31. package/lib/cjs/utils/stream-utils.d.ts +3 -2
  32. package/lib/cjs/utils/stream-utils.js +50 -26
  33. package/lib/cjs/utils/type-utils.d.ts +5 -0
  34. package/lib/dts/agents/agent.d.ts +129 -6
  35. package/lib/dts/agents/ai-agent.d.ts +3 -2
  36. package/lib/{cjs/models → dts/agents}/chat-model.d.ts +24 -13
  37. package/lib/dts/agents/guide-rail-agent.d.ts +62 -0
  38. package/lib/dts/aigne/aigne.d.ts +3 -2
  39. package/lib/dts/aigne/context.d.ts +2 -1
  40. package/lib/dts/index.d.ts +1 -1
  41. package/lib/dts/loader/agent-yaml.d.ts +1 -1
  42. package/lib/dts/loader/index.d.ts +18 -11
  43. package/lib/dts/memory/retriever.d.ts +2 -2
  44. package/lib/dts/prompt/prompt-builder.d.ts +3 -3
  45. package/lib/dts/prompt/template.d.ts +3 -3
  46. package/lib/dts/utils/logger.d.ts +33 -8
  47. package/lib/dts/utils/model-utils.d.ts +1 -1
  48. package/lib/dts/utils/stream-utils.d.ts +3 -2
  49. package/lib/dts/utils/type-utils.d.ts +5 -0
  50. package/lib/esm/agents/agent.d.ts +129 -6
  51. package/lib/esm/agents/agent.js +112 -20
  52. package/lib/esm/agents/ai-agent.d.ts +3 -2
  53. package/lib/esm/agents/ai-agent.js +12 -9
  54. package/lib/{dts/models → esm/agents}/chat-model.d.ts +24 -13
  55. package/lib/esm/{models → agents}/chat-model.js +48 -7
  56. package/lib/esm/agents/guide-rail-agent.d.ts +62 -0
  57. package/lib/esm/agents/guide-rail-agent.js +11 -0
  58. package/lib/esm/agents/mcp-agent.js +9 -9
  59. package/lib/esm/agents/team-agent.js +2 -2
  60. package/lib/esm/aigne/aigne.d.ts +3 -2
  61. package/lib/esm/aigne/aigne.js +2 -2
  62. package/lib/esm/aigne/context.d.ts +2 -1
  63. package/lib/esm/aigne/context.js +9 -2
  64. package/lib/esm/index.d.ts +1 -1
  65. package/lib/esm/index.js +1 -1
  66. package/lib/esm/loader/agent-yaml.d.ts +1 -1
  67. package/lib/esm/loader/index.d.ts +18 -11
  68. package/lib/esm/loader/index.js +8 -27
  69. package/lib/esm/memory/retriever.d.ts +2 -2
  70. package/lib/esm/prompt/prompt-builder.d.ts +3 -3
  71. package/lib/esm/prompt/template.d.ts +3 -3
  72. package/lib/esm/prompt/template.js +1 -1
  73. package/lib/esm/utils/json-schema.js +1 -1
  74. package/lib/esm/utils/logger.d.ts +33 -8
  75. package/lib/esm/utils/logger.js +61 -4
  76. package/lib/esm/utils/model-utils.d.ts +1 -1
  77. package/lib/esm/utils/stream-utils.d.ts +3 -2
  78. package/lib/esm/utils/stream-utils.js +48 -25
  79. package/lib/esm/utils/type-utils.d.ts +5 -0
  80. package/package.json +1 -20
  81. package/lib/cjs/client/client.d.ts +0 -97
  82. package/lib/cjs/client/client.js +0 -87
  83. package/lib/cjs/client/index.d.ts +0 -1
  84. package/lib/cjs/client/index.js +0 -17
  85. package/lib/cjs/models/bedrock-chat-model.d.ts +0 -79
  86. package/lib/cjs/models/bedrock-chat-model.js +0 -303
  87. package/lib/cjs/models/claude-chat-model.d.ts +0 -114
  88. package/lib/cjs/models/claude-chat-model.js +0 -317
  89. package/lib/cjs/models/deepseek-chat-model.d.ts +0 -23
  90. package/lib/cjs/models/deepseek-chat-model.js +0 -35
  91. package/lib/cjs/models/gemini-chat-model.d.ts +0 -23
  92. package/lib/cjs/models/gemini-chat-model.js +0 -35
  93. package/lib/cjs/models/ollama-chat-model.d.ts +0 -22
  94. package/lib/cjs/models/ollama-chat-model.js +0 -34
  95. package/lib/cjs/models/open-router-chat-model.d.ts +0 -22
  96. package/lib/cjs/models/open-router-chat-model.js +0 -34
  97. package/lib/cjs/models/openai-chat-model.d.ts +0 -166
  98. package/lib/cjs/models/openai-chat-model.js +0 -415
  99. package/lib/cjs/models/xai-chat-model.d.ts +0 -21
  100. package/lib/cjs/models/xai-chat-model.js +0 -33
  101. package/lib/cjs/server/error.d.ts +0 -15
  102. package/lib/cjs/server/error.js +0 -22
  103. package/lib/cjs/server/index.d.ts +0 -2
  104. package/lib/cjs/server/index.js +0 -18
  105. package/lib/cjs/server/server.d.ts +0 -135
  106. package/lib/cjs/server/server.js +0 -188
  107. package/lib/dts/client/client.d.ts +0 -97
  108. package/lib/dts/client/index.d.ts +0 -1
  109. package/lib/dts/models/bedrock-chat-model.d.ts +0 -79
  110. package/lib/dts/models/claude-chat-model.d.ts +0 -114
  111. package/lib/dts/models/deepseek-chat-model.d.ts +0 -23
  112. package/lib/dts/models/gemini-chat-model.d.ts +0 -23
  113. package/lib/dts/models/ollama-chat-model.d.ts +0 -22
  114. package/lib/dts/models/open-router-chat-model.d.ts +0 -22
  115. package/lib/dts/models/openai-chat-model.d.ts +0 -166
  116. package/lib/dts/models/xai-chat-model.d.ts +0 -21
  117. package/lib/dts/server/error.d.ts +0 -15
  118. package/lib/dts/server/index.d.ts +0 -2
  119. package/lib/dts/server/server.d.ts +0 -135
  120. package/lib/esm/client/client.d.ts +0 -97
  121. package/lib/esm/client/client.js +0 -83
  122. package/lib/esm/client/index.d.ts +0 -1
  123. package/lib/esm/client/index.js +0 -1
  124. package/lib/esm/models/bedrock-chat-model.d.ts +0 -79
  125. package/lib/esm/models/bedrock-chat-model.js +0 -298
  126. package/lib/esm/models/claude-chat-model.d.ts +0 -114
  127. package/lib/esm/models/claude-chat-model.js +0 -310
  128. package/lib/esm/models/deepseek-chat-model.d.ts +0 -23
  129. package/lib/esm/models/deepseek-chat-model.js +0 -31
  130. package/lib/esm/models/gemini-chat-model.d.ts +0 -23
  131. package/lib/esm/models/gemini-chat-model.js +0 -31
  132. package/lib/esm/models/ollama-chat-model.d.ts +0 -22
  133. package/lib/esm/models/ollama-chat-model.js +0 -30
  134. package/lib/esm/models/open-router-chat-model.d.ts +0 -22
  135. package/lib/esm/models/open-router-chat-model.js +0 -30
  136. package/lib/esm/models/openai-chat-model.d.ts +0 -166
  137. package/lib/esm/models/openai-chat-model.js +0 -405
  138. package/lib/esm/models/xai-chat-model.d.ts +0 -21
  139. package/lib/esm/models/xai-chat-model.js +0 -29
  140. package/lib/esm/server/error.d.ts +0 -15
  141. package/lib/esm/server/error.js +0 -18
  142. package/lib/esm/server/index.d.ts +0 -2
  143. package/lib/esm/server/index.js +0 -2
  144. package/lib/esm/server/server.d.ts +0 -135
  145. package/lib/esm/server/server.js +0 -181
@@ -3,7 +3,8 @@ import { ZodObject, type ZodType } from "zod";
3
3
  import type { Context } from "../aigne/context.js";
4
4
  import type { MessagePayload } from "../aigne/message-queue.js";
5
5
  import type { MemoryAgent } from "../memory/memory.js";
6
- import { type Nullish, type PromiseOrValue } from "../utils/type-utils.js";
6
+ import { type Nullish, type PromiseOrValue, type XOr } from "../utils/type-utils.js";
7
+ import type { GuideRailAgent, GuideRailAgentOutput } from "./guide-rail-agent.js";
7
8
  import { type TransferAgentOutput } from "./types.js";
8
9
  export * from "./types.js";
9
10
  /**
@@ -29,7 +30,7 @@ export type PublishTopic<O extends Message> = string | string[] | ((output: O) =
29
30
  * @template I The agent input message type
30
31
  * @template O The agent output message type
31
32
  */
32
- export interface AgentOptions<I extends Message = Message, O extends Message = Message> {
33
+ export interface AgentOptions<I extends Message = Message, O extends Message = Message> extends Partial<Pick<Agent, "guideRails" | "hooks">> {
33
34
  /**
34
35
  * Topics the agent should subscribe to
35
36
  *
@@ -139,6 +140,35 @@ export declare abstract class Agent<I extends Message = Message, O extends Messa
139
140
  * List of memories this agent can use
140
141
  */
141
142
  readonly memories: MemoryAgent[];
143
+ /**
144
+ * Lifecycle hooks for agent processing.
145
+ *
146
+ * Hooks enable tracing, logging, monitoring, and custom behavior
147
+ * without modifying the core agent implementation
148
+ *
149
+ * @example
150
+ * Here's an example of using hooks:
151
+ * {@includeCode ../../test/agents/agent.test.ts#example-agent-hooks}
152
+ */
153
+ readonly hooks: AgentHooks;
154
+ /**
155
+ * List of GuideRail agents applied to this agent
156
+ *
157
+ * GuideRail agents validate, transform, or control the message flow by:
158
+ * - Enforcing rules and safety policies
159
+ * - Validating inputs/outputs against specific criteria
160
+ * - Implementing business logic validations
161
+ * - Monitoring and auditing agent behavior
162
+ *
163
+ * Each GuideRail agent can examine both input and expected output,
164
+ * and has the ability to abort the process with an explanation
165
+ *
166
+ * @example
167
+ * Here's an example of using GuideRail agents:
168
+ *
169
+ * {@includeCode ../../test/agents/agent.test.ts#example-agent-guide-rails}
170
+ */
171
+ readonly guideRails?: GuideRailAgent[];
142
172
  /**
143
173
  * Name of the agent, used for identification and logging
144
174
  *
@@ -301,6 +331,7 @@ export declare abstract class Agent<I extends Message = Message, O extends Messa
301
331
  * @returns Agent response (streaming or regular)
302
332
  */
303
333
  invoke(input: I | string, context?: Context, options?: AgentInvokeOptions): Promise<AgentResponse<O>>;
334
+ protected invokeSkill<I extends Message, O extends Message>(skill: Agent<I, O>, input: I, context: Context): Promise<O>;
304
335
  /**
305
336
  * Process agent output
306
337
  *
@@ -319,7 +350,6 @@ export declare abstract class Agent<I extends Message = Message, O extends Messa
319
350
  *
320
351
  * @param error Caught error
321
352
  * @param context Execution context
322
- * @throws Always throws the received error
323
353
  */
324
354
  private processAgentError;
325
355
  /**
@@ -342,7 +372,25 @@ export declare abstract class Agent<I extends Message = Message, O extends Messa
342
372
  * @param _ Input message (unused)
343
373
  * @param context Execution context
344
374
  */
345
- protected preprocess(_: I, context: Context): void;
375
+ protected preprocess(_: I, context: Context): PromiseOrValue<void>;
376
+ private checkResponseByGuideRails;
377
+ private runGuideRails;
378
+ /**
379
+ * Handle errors detected by GuideRail agents
380
+ *
381
+ * This method is called when a GuideRail agent aborts the process, providing
382
+ * a way for agents to customize error handling behavior. By default, it simply
383
+ * returns the original error, but subclasses can override this method to:
384
+ * - Transform the error into a more specific response
385
+ * - Apply recovery strategies
386
+ * - Log or report the error in a custom format
387
+ * - Return a fallback output instead of an error
388
+ *
389
+ * @param error The GuideRail agent output containing abort=true and a reason
390
+ * @returns Either the original/modified error or a substitute output object
391
+ * which will be tagged with $status: "GuideRailError"
392
+ */
393
+ protected onGuideRailError(error: GuideRailAgentOutput): Promise<O | GuideRailAgentOutput>;
346
394
  /**
347
395
  * Post-processing operations after handling output
348
396
  *
@@ -354,7 +402,7 @@ export declare abstract class Agent<I extends Message = Message, O extends Messa
354
402
  * @param output Output message
355
403
  * @param context Execution context
356
404
  */
357
- protected postprocess(input: I, output: O, context: Context): void;
405
+ protected postprocess(input: I, output: O, context: Context): PromiseOrValue<void>;
358
406
  protected publishToTopics(output: Message, context: Context): Promise<void>;
359
407
  /**
360
408
  * Core processing method of the agent, must be implemented in subclasses
@@ -419,6 +467,81 @@ export declare abstract class Agent<I extends Message = Message, O extends Messa
419
467
  */
420
468
  [Symbol.asyncDispose](): Promise<void>;
421
469
  }
470
+ /**
471
+ * Lifecycle hooks for agent execution
472
+ *
473
+ * Hooks provide a way to intercept and extend agent behavior at key points during
474
+ * the agent's lifecycle, enabling custom functionality like logging, monitoring,
475
+ * tracing, error handling, and more.
476
+ */
477
+ export interface AgentHooks<I extends Message = Message, O extends Message = Message> {
478
+ /**
479
+ * Called when agent processing begins
480
+ *
481
+ * This hook runs before the agent processes input, allowing for
482
+ * setup operations, logging, or input transformations.
483
+ *
484
+ * @param event Object containing the input message
485
+ */
486
+ onStart?: (event: {
487
+ input: I;
488
+ }) => PromiseOrValue<void>;
489
+ /**
490
+ * Called when agent processing completes or fails
491
+ *
492
+ * This hook runs after processing finishes, receiving either the output
493
+ * or an error if processing failed. Useful for cleanup operations,
494
+ * logging results, or error handling.
495
+ *
496
+ * @param event Object containing the input message and either output or error
497
+ */
498
+ onEnd?: (event: XOr<{
499
+ input: I;
500
+ output: O;
501
+ error: Error;
502
+ }, "output", "error">) => PromiseOrValue<void>;
503
+ /**
504
+ * Called before a skill (sub-agent) is invoked
505
+ *
506
+ * This hook runs when the agent delegates work to a skill,
507
+ * allowing for tracking skill usage or transforming input to the skill.
508
+ *
509
+ * @param event Object containing the skill being used and input message
510
+ */
511
+ onSkillStart?: (event: {
512
+ skill: Agent;
513
+ input: I;
514
+ }) => PromiseOrValue<void>;
515
+ /**
516
+ * Called after a skill (sub-agent) completes or fails
517
+ *
518
+ * This hook runs when a skill finishes execution, receiving either the output
519
+ * or an error if the skill failed. Useful for monitoring skill performance
520
+ * or handling skill-specific errors.
521
+ *
522
+ * @param event Object containing the skill used, input message, and either output or error
523
+ */
524
+ onSkillEnd?: (event: XOr<{
525
+ skill: Agent;
526
+ input: I;
527
+ output: O;
528
+ error: Error;
529
+ }, "output", "error">) => PromiseOrValue<void>;
530
+ /**
531
+ * Called when an agent hands off processing to another agent
532
+ *
533
+ * This hook runs when a source agent transfers control to a target agent,
534
+ * allowing for tracking of handoffs between agents and monitoring the flow
535
+ * of processing in multi-agent systems.
536
+ *
537
+ * @param event Object containing the source agent, target agent, and input message
538
+ */
539
+ onHandoff?: (event: {
540
+ source: Agent;
541
+ target: Agent;
542
+ input: I;
543
+ }) => PromiseOrValue<void>;
544
+ }
422
545
  /**
423
546
  * Response type for an agent, can be:
424
547
  * - Direct response object
@@ -427,7 +550,7 @@ export declare abstract class Agent<I extends Message = Message, O extends Messa
427
550
  *
428
551
  * @template T Response data type
429
552
  */
430
- export type AgentResponse<T> = T | TransferAgentOutput | AgentResponseStream<T>;
553
+ export type AgentResponse<T> = T | AgentResponseStream<T> | TransferAgentOutput | GuideRailAgentOutput;
431
554
  /**
432
555
  * Streaming response type for an agent
433
556
  *
@@ -19,6 +19,16 @@ export const agentOptionsSchema = z.object({
19
19
  skills: z.array(z.union([z.custom(), z.custom()])).optional(),
20
20
  disableEvents: z.boolean().optional(),
21
21
  memory: z.union([z.custom(), z.array(z.custom())]).optional(),
22
+ hooks: z
23
+ .object({
24
+ onStart: z.custom().optional(),
25
+ onEnd: z.custom().optional(),
26
+ onSkillStart: z.custom().optional(),
27
+ onSkillEnd: z.custom().optional(),
28
+ onHandoff: z.custom().optional(),
29
+ })
30
+ .optional(),
31
+ guideRails: z.array(z.custom()).optional(),
22
32
  });
23
33
  /**
24
34
  * Agent is the base class for all agents.
@@ -65,11 +75,42 @@ export class Agent {
65
75
  else if (options.memory) {
66
76
  this.memories.push(options.memory);
67
77
  }
78
+ this.hooks = options.hooks ?? {};
79
+ this.guideRails = options.guideRails;
68
80
  }
69
81
  /**
70
82
  * List of memories this agent can use
71
83
  */
72
84
  memories = [];
85
+ /**
86
+ * Lifecycle hooks for agent processing.
87
+ *
88
+ * Hooks enable tracing, logging, monitoring, and custom behavior
89
+ * without modifying the core agent implementation
90
+ *
91
+ * @example
92
+ * Here's an example of using hooks:
93
+ * {@includeCode ../../test/agents/agent.test.ts#example-agent-hooks}
94
+ */
95
+ hooks;
96
+ /**
97
+ * List of GuideRail agents applied to this agent
98
+ *
99
+ * GuideRail agents validate, transform, or control the message flow by:
100
+ * - Enforcing rules and safety policies
101
+ * - Validating inputs/outputs against specific criteria
102
+ * - Implementing business logic validations
103
+ * - Monitoring and auditing agent behavior
104
+ *
105
+ * Each GuideRail agent can examine both input and expected output,
106
+ * and has the ability to abort the process with an explanation
107
+ *
108
+ * @example
109
+ * Here's an example of using GuideRail agents:
110
+ *
111
+ * {@includeCode ../../test/agents/agent.test.ts#example-agent-guide-rails}
112
+ */
113
+ guideRails;
73
114
  /**
74
115
  * Name of the agent, used for identification and logging
75
116
  *
@@ -226,12 +267,13 @@ export class Agent {
226
267
  async invoke(input, context, options) {
227
268
  const ctx = context ?? (await this.newDefaultContext());
228
269
  const message = typeof input === "string" ? createMessage(input) : input;
229
- logger.core("Invoke agent %s started with input: %O", this.name, input);
270
+ logger.debug("Invoke agent %s started with input: %O", this.name, input);
230
271
  if (!this.disableEvents)
231
272
  ctx.emit("agentStarted", { agent: this, input: message });
232
273
  try {
274
+ await this.hooks.onStart?.({ input: message });
233
275
  const parsedInput = checkArguments(`Agent ${this.name} input`, this.inputSchema, message);
234
- this.preprocess(parsedInput, ctx);
276
+ await this.preprocess(parsedInput, ctx);
235
277
  this.checkContextStatus(ctx);
236
278
  let response = await this.process(parsedInput, ctx);
237
279
  if (response instanceof Agent) {
@@ -243,27 +285,34 @@ export class Agent {
243
285
  : isAsyncGenerator(response)
244
286
  ? asyncGeneratorToReadableStream(response)
245
287
  : objectToAgentResponseStream(response);
246
- return onAgentResponseStreamEnd(stream, async (result) => {
288
+ return this.checkResponseByGuideRails(message, onAgentResponseStreamEnd(stream, async (result) => {
247
289
  return await this.processAgentOutput(parsedInput, result, ctx);
248
290
  }, {
249
- errorCallback: (error) => {
250
- try {
251
- this.processAgentError(error, ctx);
252
- }
253
- catch (error) {
254
- return error;
255
- }
291
+ errorCallback: async (error) => {
292
+ return await this.processAgentError(message, error, ctx);
256
293
  },
257
- });
294
+ }), ctx);
258
295
  }
259
- return await this.processAgentOutput(parsedInput, response instanceof ReadableStream
296
+ return await this.checkResponseByGuideRails(message, this.processAgentOutput(parsedInput, response instanceof ReadableStream
260
297
  ? await agentResponseStreamToObject(response)
261
298
  : isAsyncGenerator(response)
262
299
  ? await agentResponseStreamToObject(response)
263
- : response, ctx);
300
+ : response, ctx), ctx);
264
301
  }
265
302
  catch (error) {
266
- this.processAgentError(error, ctx);
303
+ throw await this.processAgentError(message, error, ctx);
304
+ }
305
+ }
306
+ async invokeSkill(skill, input, context) {
307
+ await this.hooks.onSkillStart?.({ skill, input });
308
+ try {
309
+ const output = await context.invoke(skill, input);
310
+ await this.hooks.onSkillEnd?.({ skill, input, output });
311
+ return output;
312
+ }
313
+ catch (error) {
314
+ await this.hooks.onSkillEnd?.({ skill, input, error });
315
+ throw error;
267
316
  }
268
317
  }
269
318
  /**
@@ -279,10 +328,11 @@ export class Agent {
279
328
  async processAgentOutput(input, output, context) {
280
329
  const parsedOutput = checkArguments(`Agent ${this.name} output`, this.outputSchema, output);
281
330
  const finalOutput = this.includeInputInOutput ? { ...input, ...parsedOutput } : parsedOutput;
282
- this.postprocess(input, finalOutput, context);
283
- logger.core("Invoke agent %s succeed with output: %O", this.name, finalOutput);
331
+ await this.postprocess(input, finalOutput, context);
332
+ logger.debug("Invoke agent %s succeed with output: %O", this.name, finalOutput);
284
333
  if (!this.disableEvents)
285
334
  context.emit("agentSucceed", { agent: this, output: finalOutput });
335
+ await this.hooks.onEnd?.({ input, output: finalOutput });
286
336
  return finalOutput;
287
337
  }
288
338
  /**
@@ -292,13 +342,13 @@ export class Agent {
292
342
  *
293
343
  * @param error Caught error
294
344
  * @param context Execution context
295
- * @throws Always throws the received error
296
345
  */
297
- processAgentError(error, context) {
298
- logger.core("Invoke agent %s failed with error: %O", this.name, error);
346
+ async processAgentError(input, error, context) {
347
+ logger.error("Invoke agent %s failed with error: %O", this.name, error);
299
348
  if (!this.disableEvents)
300
349
  context.emit("agentFailed", { agent: this, error });
301
- throw error;
350
+ await this.hooks.onEnd?.({ input, error });
351
+ return error;
302
352
  }
303
353
  /**
304
354
  * Check agent invocation usage to prevent exceeding limits
@@ -330,6 +380,48 @@ export class Agent {
330
380
  this.checkContextStatus(context);
331
381
  this.checkAgentInvokesUsage(context);
332
382
  }
383
+ async checkResponseByGuideRails(input, output, context) {
384
+ if (!this.guideRails?.length)
385
+ return output;
386
+ const result = await output;
387
+ if (result instanceof ReadableStream) {
388
+ return onAgentResponseStreamEnd(result, async (result) => {
389
+ const error = await this.runGuideRails(input, result, context);
390
+ if (error) {
391
+ return {
392
+ ...(await this.onGuideRailError(error)),
393
+ $status: "GuideRailError",
394
+ };
395
+ }
396
+ });
397
+ }
398
+ const error = await this.runGuideRails(input, result, context);
399
+ if (!error)
400
+ return output;
401
+ return { ...(await this.onGuideRailError(error)), $status: "GuideRailError" };
402
+ }
403
+ async runGuideRails(input, output, context) {
404
+ const result = await Promise.all((this.guideRails ?? []).map((i) => context.invoke(i, { input, output })));
405
+ return result.find((i) => !!i.abort);
406
+ }
407
+ /**
408
+ * Handle errors detected by GuideRail agents
409
+ *
410
+ * This method is called when a GuideRail agent aborts the process, providing
411
+ * a way for agents to customize error handling behavior. By default, it simply
412
+ * returns the original error, but subclasses can override this method to:
413
+ * - Transform the error into a more specific response
414
+ * - Apply recovery strategies
415
+ * - Log or report the error in a custom format
416
+ * - Return a fallback output instead of an error
417
+ *
418
+ * @param error The GuideRail agent output containing abort=true and a reason
419
+ * @returns Either the original/modified error or a substitute output object
420
+ * which will be tagged with $status: "GuideRailError"
421
+ */
422
+ async onGuideRailError(error) {
423
+ return error;
424
+ }
333
425
  /**
334
426
  * Post-processing operations after handling output
335
427
  *
@@ -1,10 +1,10 @@
1
1
  import { type ZodObject, type ZodType, z } from "zod";
2
2
  import type { Context } from "../aigne/context.js";
3
3
  import { type DefaultMemoryOptions } from "../memory/default-memory.js";
4
- import { ChatModel } from "../models/chat-model.js";
5
- import type { ChatModelInput } from "../models/chat-model.js";
6
4
  import { PromptBuilder } from "../prompt/prompt-builder.js";
7
5
  import { Agent, type AgentOptions, type AgentProcessAsyncGenerator, type Message } from "./agent.js";
6
+ import { ChatModel, type ChatModelInput } from "./chat-model.js";
7
+ import type { GuideRailAgentOutput } from "./guide-rail-agent.js";
8
8
  /**
9
9
  * Configuration options for an AI Agent
10
10
  *
@@ -216,6 +216,7 @@ export declare class AIAgent<I extends Message = Message, O extends Message = Me
216
216
  * @protected
217
217
  */
218
218
  process(input: I, context: Context): AgentProcessAsyncGenerator<O>;
219
+ protected onGuideRailError(error: GuideRailAgentOutput): Promise<O | GuideRailAgentOutput>;
219
220
  /**
220
221
  * Process router mode requests
221
222
  *
@@ -1,12 +1,11 @@
1
1
  import { z } from "zod";
2
2
  import { DefaultMemory } from "../memory/default-memory.js";
3
3
  import { MemoryAgent } from "../memory/memory.js";
4
- import { ChatModel } from "../models/chat-model.js";
5
4
  import { MESSAGE_KEY, PromptBuilder } from "../prompt/prompt-builder.js";
6
5
  import { AgentMessageTemplate, ToolMessageTemplate } from "../prompt/template.js";
7
- import { readableStreamToAsyncIterator } from "../utils/stream-utils.js";
8
6
  import { checkArguments, isEmpty } from "../utils/type-utils.js";
9
7
  import { Agent, agentOptionsSchema, } from "./agent.js";
8
+ import { ChatModel, } from "./chat-model.js";
10
9
  import { isTransferAgentOutput } from "./types.js";
11
10
  /**
12
11
  * Tool choice options for AI agents
@@ -206,7 +205,7 @@ export class AIAgent extends Agent {
206
205
  for (;;) {
207
206
  const modelOutput = {};
208
207
  const stream = await context.invoke(model, { ...modelInput, messages: modelInput.messages.concat(toolCallMessages) }, { streaming: true });
209
- for await (const value of readableStreamToAsyncIterator(stream)) {
208
+ for await (const value of stream) {
210
209
  if (value.delta.text?.text) {
211
210
  yield { delta: { text: { [outputKey]: value.delta.text.text } } };
212
211
  }
@@ -223,9 +222,7 @@ export class AIAgent extends Agent {
223
222
  if (!tool)
224
223
  throw new Error(`Tool not found: ${call.function.name}`);
225
224
  // NOTE: should pass both arguments (model generated) and input (user provided) to the tool
226
- const output = await context
227
- .invoke(tool, { ...input, ...call.function.arguments }, { disableTransfer: true })
228
- .catch((error) => {
225
+ const output = await this.invokeSkill(tool, { ...input, ...call.function.arguments }, context).catch((error) => {
229
226
  if (!this.catchToolsError) {
230
227
  return Promise.reject(error);
231
228
  }
@@ -249,7 +246,7 @@ export class AIAgent extends Agent {
249
246
  }
250
247
  }
251
248
  const result = {};
252
- if (modelInput.responseFormat?.type === "json_schema") {
249
+ if (json) {
253
250
  Object.assign(result, json);
254
251
  }
255
252
  else if (text) {
@@ -261,6 +258,12 @@ export class AIAgent extends Agent {
261
258
  return;
262
259
  }
263
260
  }
261
+ async onGuideRailError(error) {
262
+ const outputKey = this.outputKey || MESSAGE_KEY;
263
+ return {
264
+ [outputKey]: error.reason,
265
+ };
266
+ }
264
267
  /**
265
268
  * Process router mode requests
266
269
  *
@@ -277,7 +280,7 @@ export class AIAgent extends Agent {
277
280
  const tool = toolsMap.get(call.function.name);
278
281
  if (!tool)
279
282
  throw new Error(`Tool not found: ${call.function.name}`);
280
- const stream = await context.invoke(tool, { ...call.function.arguments, ...input }, { streaming: true });
281
- yield* readableStreamToAsyncIterator(stream);
283
+ const stream = await context.invoke(tool, { ...call.function.arguments, ...input }, { streaming: true, sourceAgent: this });
284
+ yield* stream;
282
285
  }
283
286
  }
@@ -1,6 +1,6 @@
1
- import { Agent, type AgentProcessResult, type Message } from "../agents/agent.js";
2
1
  import type { Context } from "../aigne/context.js";
3
2
  import type { PromiseOrValue } from "../utils/type-utils.js";
3
+ import { Agent, type AgentProcessResult, type Message } from "./agent.js";
4
4
  /**
5
5
  * ChatModel is an abstract base class for interacting with Large Language Models (LLMs).
6
6
  *
@@ -10,19 +10,19 @@ import type { PromiseOrValue } from "../utils/type-utils.js";
10
10
  *
11
11
  * @example
12
12
  * Here's how to implement a custom ChatModel:
13
- * {@includeCode ../../test/models/chat-model.test.ts#example-chat-model}
13
+ * {@includeCode ../../test/agents/chat-model.test.ts#example-chat-model}
14
14
  *
15
15
  * @example
16
16
  * Here's an example showing streaming response with readable stream:
17
- * {@includeCode ../../test/models/chat-model.test.ts#example-chat-model-streaming}
17
+ * {@includeCode ../../test/agents/chat-model.test.ts#example-chat-model-streaming}
18
18
  *
19
19
  * @example
20
20
  * Here's an example showing streaming response with async generator:
21
- * {@includeCode ../../test/models/chat-model.test.ts#example-chat-model-streaming-async-generator}
21
+ * {@includeCode ../../test/agents/chat-model.test.ts#example-chat-model-streaming-async-generator}
22
22
  *
23
23
  * @example
24
24
  * Here's an example with tool calls:
25
- * {@includeCode ../../test/models/chat-model.test.ts#example-chat-model-tools}
25
+ * {@includeCode ../../test/agents/chat-model.test.ts#example-chat-model-tools}
26
26
  */
27
27
  export declare abstract class ChatModel extends Agent<ChatModelInput, ChatModelOutput> {
28
28
  constructor();
@@ -44,6 +44,17 @@ export declare abstract class ChatModel extends Agent<ChatModelInput, ChatModelO
44
44
  supportsParallelToolCalls: boolean;
45
45
  };
46
46
  private validateToolNames;
47
+ /**
48
+ * Normalizes tool names to ensure compatibility with language models
49
+ *
50
+ * This method converts tool names to a format that complies with model requirements
51
+ * by replacing hyphens and whitespace characters with underscores. The normalized
52
+ * names are used for tool calls while preserving the original names for reference.
53
+ *
54
+ * @param name - The original tool name to normalize
55
+ * @returns A promise that resolves to the normalized tool name
56
+ */
57
+ protected normalizeToolName(name: string): Promise<string>;
47
58
  /**
48
59
  * Performs preprocessing operations before handling input
49
60
  *
@@ -53,7 +64,7 @@ export declare abstract class ChatModel extends Agent<ChatModelInput, ChatModelO
53
64
  * @param context Execution context
54
65
  * @throws Error if token usage exceeds maximum limit
55
66
  */
56
- protected preprocess(input: ChatModelInput, context: Context): void;
67
+ protected preprocess(input: ChatModelInput, context: Context): Promise<void>;
57
68
  /**
58
69
  * Performs postprocessing operations after handling output
59
70
  *
@@ -94,11 +105,11 @@ export declare abstract class ChatModel extends Agent<ChatModelInput, ChatModelO
94
105
  *
95
106
  * @example
96
107
  * Here's a basic ChatModel input example:
97
- * {@includeCode ../../test/models/chat-model.test.ts#example-chat-model}
108
+ * {@includeCode ../../test/agents/chat-model.test.ts#example-chat-model}
98
109
  *
99
110
  * @example
100
111
  * Here's an example with tool calling:
101
- * {@includeCode ../../test/models/chat-model.test.ts#example-chat-model-tools}
112
+ * {@includeCode ../../test/agents/chat-model.test.ts#example-chat-model-tools}
102
113
  */
103
114
  export interface ChatModelInput extends Message {
104
115
  /**
@@ -213,7 +224,7 @@ export type ChatModelInputResponseFormat = {
213
224
  *
214
225
  * @example
215
226
  * Here's an example showing how to use tools:
216
- * {@includeCode ../../test/models/chat-model.test.ts#example-chat-model-tools}
227
+ * {@includeCode ../../test/agents/chat-model.test.ts#example-chat-model-tools}
217
228
  */
218
229
  export interface ChatModelInputTool {
219
230
  /**
@@ -249,7 +260,7 @@ export interface ChatModelInputTool {
249
260
  *
250
261
  * @example
251
262
  * Here's an example showing how to use tools:
252
- * {@includeCode ../../test/models/chat-model.test.ts#example-chat-model-tools}
263
+ * {@includeCode ../../test/agents/chat-model.test.ts#example-chat-model-tools}
253
264
  */
254
265
  export type ChatModelInputToolChoice = "auto" | "none" | "required" | {
255
266
  type: "function";
@@ -296,11 +307,11 @@ export interface ChatModelOptions {
296
307
  *
297
308
  * @example
298
309
  * Here's a basic output example:
299
- * {@includeCode ../../test/models/chat-model.test.ts#example-chat-model}
310
+ * {@includeCode ../../test/agents/chat-model.test.ts#example-chat-model}
300
311
  *
301
312
  * @example
302
313
  * Here's an example with tool calls:
303
- * {@includeCode ../../test/models/chat-model.test.ts#example-chat-model-tools}
314
+ * {@includeCode ../../test/agents/chat-model.test.ts#example-chat-model-tools}
304
315
  */
305
316
  export interface ChatModelOutput extends Message {
306
317
  /**
@@ -331,7 +342,7 @@ export interface ChatModelOutput extends Message {
331
342
  *
332
343
  * @example
333
344
  * Here's an example with tool calls:
334
- * {@includeCode ../../test/models/chat-model.test.ts#example-chat-model-tools}
345
+ * {@includeCode ../../test/agents/chat-model.test.ts#example-chat-model-tools}
335
346
  */
336
347
  export interface ChatModelOutputToolCall {
337
348
  /**