@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
package/CHANGELOG.md CHANGED
@@ -22,6 +22,33 @@
22
22
  * rename @aigne/core-next to @aigne/core ([3a81009](https://github.com/AIGNE-io/aigne-framework/commit/3a8100962c81813217b687ae28e8de604419c622))
23
23
  * use text resource from MCP correctly ([8b9eba8](https://github.com/AIGNE-io/aigne-framework/commit/8b9eba83352ec096a2a5d4f410d4c4bde7420bce))
24
24
 
25
+ ## [1.16.0](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.15.0...core-v1.16.0) (2025-05-23)
26
+
27
+
28
+ ### Features
29
+
30
+ * add `--chat` option for `run` command ([#120](https://github.com/AIGNE-io/aigne-framework/issues/120)) ([7699550](https://github.com/AIGNE-io/aigne-framework/commit/76995507001ca33b09b29f72ff588dae513cb340))
31
+ * **core:** support check output with guide rail agents ([#117](https://github.com/AIGNE-io/aigne-framework/issues/117)) ([bdf7ab3](https://github.com/AIGNE-io/aigne-framework/commit/bdf7ab31789379ba5b0fd920541a469cb86150ff))
32
+ * **core:** support lifecycle hooks for agent ([#124](https://github.com/AIGNE-io/aigne-framework/issues/124)) ([0af6afa](https://github.com/AIGNE-io/aigne-framework/commit/0af6afa923dcb917d545fd4535cabe7804fa84c9))
33
+ * **models:** publish model adapters as standalone packages ([#126](https://github.com/AIGNE-io/aigne-framework/issues/126)) ([588b8ae](https://github.com/AIGNE-io/aigne-framework/commit/588b8aea6abcee5fa87def1358bf51f84021c6ef))
34
+
35
+
36
+ ### Bug Fixes
37
+
38
+ * automatically convert tool names to a valid format ([#128](https://github.com/AIGNE-io/aigne-framework/issues/128)) ([e9ee91d](https://github.com/AIGNE-io/aigne-framework/commit/e9ee91d9d782fa19000adb4cf95b9d65196ab651))
39
+
40
+ ## [1.15.0](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.14.0...core-v1.15.0) (2025-05-15)
41
+
42
+
43
+ ### Features
44
+
45
+ * optimize the stability of the model and ci ([#119](https://github.com/AIGNE-io/aigne-framework/issues/119)) ([de93887](https://github.com/AIGNE-io/aigne-framework/commit/de938879452a8be82a198dda0eda1eb9fcbb0474))
46
+
47
+
48
+ ### Bug Fixes
49
+
50
+ * **core:** response.headers.toJSON is not a function ([#121](https://github.com/AIGNE-io/aigne-framework/issues/121)) ([4609ba6](https://github.com/AIGNE-io/aigne-framework/commit/4609ba645e6b8fe8d76ecd475cd2d7817483a4bd))
51
+
25
52
  ## [1.14.0](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.13.0...core-v1.14.0) (2025-05-12)
26
53
 
27
54
 
package/README.md CHANGED
@@ -16,12 +16,12 @@ Core library of [AIGNE Framework](https://github.com/AIGNE-io/aigne-framework) f
16
16
 
17
17
  ## Features
18
18
 
19
- - **Multiple AI Model Support**: Built-in support for OpenAI, Gemini, Claude, Nova, and other mainstream AI models, easily extensible to support additional models
20
- - **Agent System**: Powerful agent abstractions supporting AI agents, function agents, MCP agents, and more
21
- - **AIGNE Environment**: Flexible handling communication between agents and workflow execution
22
- - **Workflow Patterns**: Support for sequential, concurrent, routing, handoff, and other workflow patterns
23
- - **MCP Protocol Integration**: Seamless integration with external systems through the Model Context Protocol
24
- - **TypeScript Support**: Comprehensive type definitions providing an excellent development experience
19
+ * **Multiple AI Model Support**: Built-in support for OpenAI, Gemini, Claude, Nova, and other mainstream AI models, easily extensible to support additional models
20
+ * **Agent System**: Powerful agent abstractions supporting AI agents, function agents, MCP agents, and more
21
+ * **AIGNE Environment**: Flexible handling communication between agents and workflow execution
22
+ * **Workflow Patterns**: Support for sequential, concurrent, routing, handoff, and other workflow patterns
23
+ * **MCP Protocol Integration**: Seamless integration with external systems through the Model Context Protocol
24
+ * **TypeScript Support**: Comprehensive type definitions providing an excellent development experience
25
25
 
26
26
  ## Installation
27
27
 
@@ -68,7 +68,9 @@ const aigne = new AIGNE({ model });
68
68
  const userAgent = await aigne.invoke(agent);
69
69
 
70
70
  // Send a message to the agent
71
- const response = await userAgent.invoke("Hello, can you help me write a short article?");
71
+ const response = await userAgent.invoke(
72
+ "Hello, can you help me write a short article?",
73
+ );
72
74
  console.log(response);
73
75
  ```
74
76
 
package/README.zh.md CHANGED
@@ -16,12 +16,12 @@
16
16
 
17
17
  ## 特性
18
18
 
19
- - **多 AI 模型支持**:内置支持 OpenAI、Gemini、Claude、Nova 等主流 AI 模型,可轻松扩展支持其他模型
20
- - **代理系统**:强大的代理抽象,支持 AI 代理、功能代理、MCP 代理等
21
- - **AIGNE 环境**:灵活处理代理之间的通信和工作流执行
22
- - **工作流模式**:支持顺序、并发、路由、交接等多种工作流模式
23
- - **MCP 协议集成**:通过模型上下文协议与外部系统无缝集成
24
- - **TypeScript 支持**:全面的类型定义,提供出色的开发体验
19
+ * **多 AI 模型支持**:内置支持 OpenAI、Gemini、Claude、Nova 等主流 AI 模型,可轻松扩展支持其他模型
20
+ * **代理系统**:强大的代理抽象,支持 AI 代理、功能代理、MCP 代理等
21
+ * **AIGNE 环境**:灵活处理代理之间的通信和工作流执行
22
+ * **工作流模式**:支持顺序、并发、路由、交接等多种工作流模式
23
+ * **MCP 协议集成**:通过模型上下文协议与外部系统无缝集成
24
+ * **TypeScript 支持**:全面的类型定义,提供出色的开发体验
25
25
 
26
26
  ## 安装
27
27
 
@@ -68,7 +68,9 @@ const aigne = new AIGNE({ model });
68
68
  const userAgent = await aigne.invoke(agent);
69
69
 
70
70
  // 向代理发送消息
71
- const response = await userAgent.invoke("Hello, can you help me write a short article?");
71
+ const response = await userAgent.invoke(
72
+ "Hello, can you help me write a short article?",
73
+ );
72
74
  console.log(response);
73
75
  ```
74
76
 
@@ -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
  *
@@ -61,6 +61,16 @@ exports.agentOptionsSchema = zod_1.z.object({
61
61
  skills: zod_1.z.array(zod_1.z.union([zod_1.z.custom(), zod_1.z.custom()])).optional(),
62
62
  disableEvents: zod_1.z.boolean().optional(),
63
63
  memory: zod_1.z.union([zod_1.z.custom(), zod_1.z.array(zod_1.z.custom())]).optional(),
64
+ hooks: zod_1.z
65
+ .object({
66
+ onStart: zod_1.z.custom().optional(),
67
+ onEnd: zod_1.z.custom().optional(),
68
+ onSkillStart: zod_1.z.custom().optional(),
69
+ onSkillEnd: zod_1.z.custom().optional(),
70
+ onHandoff: zod_1.z.custom().optional(),
71
+ })
72
+ .optional(),
73
+ guideRails: zod_1.z.array(zod_1.z.custom()).optional(),
64
74
  });
65
75
  /**
66
76
  * Agent is the base class for all agents.
@@ -107,11 +117,42 @@ class Agent {
107
117
  else if (options.memory) {
108
118
  this.memories.push(options.memory);
109
119
  }
120
+ this.hooks = options.hooks ?? {};
121
+ this.guideRails = options.guideRails;
110
122
  }
111
123
  /**
112
124
  * List of memories this agent can use
113
125
  */
114
126
  memories = [];
127
+ /**
128
+ * Lifecycle hooks for agent processing.
129
+ *
130
+ * Hooks enable tracing, logging, monitoring, and custom behavior
131
+ * without modifying the core agent implementation
132
+ *
133
+ * @example
134
+ * Here's an example of using hooks:
135
+ * {@includeCode ../../test/agents/agent.test.ts#example-agent-hooks}
136
+ */
137
+ hooks;
138
+ /**
139
+ * List of GuideRail agents applied to this agent
140
+ *
141
+ * GuideRail agents validate, transform, or control the message flow by:
142
+ * - Enforcing rules and safety policies
143
+ * - Validating inputs/outputs against specific criteria
144
+ * - Implementing business logic validations
145
+ * - Monitoring and auditing agent behavior
146
+ *
147
+ * Each GuideRail agent can examine both input and expected output,
148
+ * and has the ability to abort the process with an explanation
149
+ *
150
+ * @example
151
+ * Here's an example of using GuideRail agents:
152
+ *
153
+ * {@includeCode ../../test/agents/agent.test.ts#example-agent-guide-rails}
154
+ */
155
+ guideRails;
115
156
  /**
116
157
  * Name of the agent, used for identification and logging
117
158
  *
@@ -268,12 +309,13 @@ class Agent {
268
309
  async invoke(input, context, options) {
269
310
  const ctx = context ?? (await this.newDefaultContext());
270
311
  const message = typeof input === "string" ? (0, prompt_builder_js_1.createMessage)(input) : input;
271
- logger_js_1.logger.core("Invoke agent %s started with input: %O", this.name, input);
312
+ logger_js_1.logger.debug("Invoke agent %s started with input: %O", this.name, input);
272
313
  if (!this.disableEvents)
273
314
  ctx.emit("agentStarted", { agent: this, input: message });
274
315
  try {
316
+ await this.hooks.onStart?.({ input: message });
275
317
  const parsedInput = (0, type_utils_js_1.checkArguments)(`Agent ${this.name} input`, this.inputSchema, message);
276
- this.preprocess(parsedInput, ctx);
318
+ await this.preprocess(parsedInput, ctx);
277
319
  this.checkContextStatus(ctx);
278
320
  let response = await this.process(parsedInput, ctx);
279
321
  if (response instanceof Agent) {
@@ -285,27 +327,34 @@ class Agent {
285
327
  : (0, stream_utils_js_1.isAsyncGenerator)(response)
286
328
  ? (0, stream_utils_js_1.asyncGeneratorToReadableStream)(response)
287
329
  : (0, stream_utils_js_1.objectToAgentResponseStream)(response);
288
- return (0, stream_utils_js_1.onAgentResponseStreamEnd)(stream, async (result) => {
330
+ return this.checkResponseByGuideRails(message, (0, stream_utils_js_1.onAgentResponseStreamEnd)(stream, async (result) => {
289
331
  return await this.processAgentOutput(parsedInput, result, ctx);
290
332
  }, {
291
- errorCallback: (error) => {
292
- try {
293
- this.processAgentError(error, ctx);
294
- }
295
- catch (error) {
296
- return error;
297
- }
333
+ errorCallback: async (error) => {
334
+ return await this.processAgentError(message, error, ctx);
298
335
  },
299
- });
336
+ }), ctx);
300
337
  }
301
- return await this.processAgentOutput(parsedInput, response instanceof ReadableStream
338
+ return await this.checkResponseByGuideRails(message, this.processAgentOutput(parsedInput, response instanceof ReadableStream
302
339
  ? await (0, stream_utils_js_1.agentResponseStreamToObject)(response)
303
340
  : (0, stream_utils_js_1.isAsyncGenerator)(response)
304
341
  ? await (0, stream_utils_js_1.agentResponseStreamToObject)(response)
305
- : response, ctx);
342
+ : response, ctx), ctx);
306
343
  }
307
344
  catch (error) {
308
- this.processAgentError(error, ctx);
345
+ throw await this.processAgentError(message, error, ctx);
346
+ }
347
+ }
348
+ async invokeSkill(skill, input, context) {
349
+ await this.hooks.onSkillStart?.({ skill, input });
350
+ try {
351
+ const output = await context.invoke(skill, input);
352
+ await this.hooks.onSkillEnd?.({ skill, input, output });
353
+ return output;
354
+ }
355
+ catch (error) {
356
+ await this.hooks.onSkillEnd?.({ skill, input, error });
357
+ throw error;
309
358
  }
310
359
  }
311
360
  /**
@@ -321,10 +370,11 @@ class Agent {
321
370
  async processAgentOutput(input, output, context) {
322
371
  const parsedOutput = (0, type_utils_js_1.checkArguments)(`Agent ${this.name} output`, this.outputSchema, output);
323
372
  const finalOutput = this.includeInputInOutput ? { ...input, ...parsedOutput } : parsedOutput;
324
- this.postprocess(input, finalOutput, context);
325
- logger_js_1.logger.core("Invoke agent %s succeed with output: %O", this.name, finalOutput);
373
+ await this.postprocess(input, finalOutput, context);
374
+ logger_js_1.logger.debug("Invoke agent %s succeed with output: %O", this.name, finalOutput);
326
375
  if (!this.disableEvents)
327
376
  context.emit("agentSucceed", { agent: this, output: finalOutput });
377
+ await this.hooks.onEnd?.({ input, output: finalOutput });
328
378
  return finalOutput;
329
379
  }
330
380
  /**
@@ -334,13 +384,13 @@ class Agent {
334
384
  *
335
385
  * @param error Caught error
336
386
  * @param context Execution context
337
- * @throws Always throws the received error
338
387
  */
339
- processAgentError(error, context) {
340
- logger_js_1.logger.core("Invoke agent %s failed with error: %O", this.name, error);
388
+ async processAgentError(input, error, context) {
389
+ logger_js_1.logger.error("Invoke agent %s failed with error: %O", this.name, error);
341
390
  if (!this.disableEvents)
342
391
  context.emit("agentFailed", { agent: this, error });
343
- throw error;
392
+ await this.hooks.onEnd?.({ input, error });
393
+ return error;
344
394
  }
345
395
  /**
346
396
  * Check agent invocation usage to prevent exceeding limits
@@ -372,6 +422,48 @@ class Agent {
372
422
  this.checkContextStatus(context);
373
423
  this.checkAgentInvokesUsage(context);
374
424
  }
425
+ async checkResponseByGuideRails(input, output, context) {
426
+ if (!this.guideRails?.length)
427
+ return output;
428
+ const result = await output;
429
+ if (result instanceof ReadableStream) {
430
+ return (0, stream_utils_js_1.onAgentResponseStreamEnd)(result, async (result) => {
431
+ const error = await this.runGuideRails(input, result, context);
432
+ if (error) {
433
+ return {
434
+ ...(await this.onGuideRailError(error)),
435
+ $status: "GuideRailError",
436
+ };
437
+ }
438
+ });
439
+ }
440
+ const error = await this.runGuideRails(input, result, context);
441
+ if (!error)
442
+ return output;
443
+ return { ...(await this.onGuideRailError(error)), $status: "GuideRailError" };
444
+ }
445
+ async runGuideRails(input, output, context) {
446
+ const result = await Promise.all((this.guideRails ?? []).map((i) => context.invoke(i, { input, output })));
447
+ return result.find((i) => !!i.abort);
448
+ }
449
+ /**
450
+ * Handle errors detected by GuideRail agents
451
+ *
452
+ * This method is called when a GuideRail agent aborts the process, providing
453
+ * a way for agents to customize error handling behavior. By default, it simply
454
+ * returns the original error, but subclasses can override this method to:
455
+ * - Transform the error into a more specific response
456
+ * - Apply recovery strategies
457
+ * - Log or report the error in a custom format
458
+ * - Return a fallback output instead of an error
459
+ *
460
+ * @param error The GuideRail agent output containing abort=true and a reason
461
+ * @returns Either the original/modified error or a substitute output object
462
+ * which will be tagged with $status: "GuideRailError"
463
+ */
464
+ async onGuideRailError(error) {
465
+ return error;
466
+ }
375
467
  /**
376
468
  * Post-processing operations after handling output
377
469
  *
@@ -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
  *
@@ -4,12 +4,11 @@ exports.AIAgent = exports.aiAgentOptionsSchema = exports.aiAgentToolChoiceSchema
4
4
  const zod_1 = require("zod");
5
5
  const default_memory_js_1 = require("../memory/default-memory.js");
6
6
  const memory_js_1 = require("../memory/memory.js");
7
- const chat_model_js_1 = require("../models/chat-model.js");
8
7
  const prompt_builder_js_1 = require("../prompt/prompt-builder.js");
9
8
  const template_js_1 = require("../prompt/template.js");
10
- const stream_utils_js_1 = require("../utils/stream-utils.js");
11
9
  const type_utils_js_1 = require("../utils/type-utils.js");
12
10
  const agent_js_1 = require("./agent.js");
11
+ const chat_model_js_1 = require("./chat-model.js");
13
12
  const types_js_1 = require("./types.js");
14
13
  /**
15
14
  * Tool choice options for AI agents
@@ -209,7 +208,7 @@ class AIAgent extends agent_js_1.Agent {
209
208
  for (;;) {
210
209
  const modelOutput = {};
211
210
  const stream = await context.invoke(model, { ...modelInput, messages: modelInput.messages.concat(toolCallMessages) }, { streaming: true });
212
- for await (const value of (0, stream_utils_js_1.readableStreamToAsyncIterator)(stream)) {
211
+ for await (const value of stream) {
213
212
  if (value.delta.text?.text) {
214
213
  yield { delta: { text: { [outputKey]: value.delta.text.text } } };
215
214
  }
@@ -226,9 +225,7 @@ class AIAgent extends agent_js_1.Agent {
226
225
  if (!tool)
227
226
  throw new Error(`Tool not found: ${call.function.name}`);
228
227
  // NOTE: should pass both arguments (model generated) and input (user provided) to the tool
229
- const output = await context
230
- .invoke(tool, { ...input, ...call.function.arguments }, { disableTransfer: true })
231
- .catch((error) => {
228
+ const output = await this.invokeSkill(tool, { ...input, ...call.function.arguments }, context).catch((error) => {
232
229
  if (!this.catchToolsError) {
233
230
  return Promise.reject(error);
234
231
  }
@@ -252,7 +249,7 @@ class AIAgent extends agent_js_1.Agent {
252
249
  }
253
250
  }
254
251
  const result = {};
255
- if (modelInput.responseFormat?.type === "json_schema") {
252
+ if (json) {
256
253
  Object.assign(result, json);
257
254
  }
258
255
  else if (text) {
@@ -264,6 +261,12 @@ class AIAgent extends agent_js_1.Agent {
264
261
  return;
265
262
  }
266
263
  }
264
+ async onGuideRailError(error) {
265
+ const outputKey = this.outputKey || prompt_builder_js_1.MESSAGE_KEY;
266
+ return {
267
+ [outputKey]: error.reason,
268
+ };
269
+ }
267
270
  /**
268
271
  * Process router mode requests
269
272
  *
@@ -280,8 +283,8 @@ class AIAgent extends agent_js_1.Agent {
280
283
  const tool = toolsMap.get(call.function.name);
281
284
  if (!tool)
282
285
  throw new Error(`Tool not found: ${call.function.name}`);
283
- const stream = await context.invoke(tool, { ...call.function.arguments, ...input }, { streaming: true });
284
- yield* (0, stream_utils_js_1.readableStreamToAsyncIterator)(stream);
286
+ const stream = await context.invoke(tool, { ...call.function.arguments, ...input }, { streaming: true, sourceAgent: this });
287
+ yield* stream;
285
288
  }
286
289
  }
287
290
  exports.AIAgent = AIAgent;