@aigne/core 1.10.0 → 1.12.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 (142) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/LICENSE +93 -0
  3. package/README.md +90 -0
  4. package/README.zh.md +90 -0
  5. package/lib/cjs/agents/agent.d.ts +21 -20
  6. package/lib/cjs/agents/agent.js +29 -26
  7. package/lib/cjs/agents/ai-agent.d.ts +9 -8
  8. package/lib/cjs/agents/ai-agent.js +20 -14
  9. package/lib/cjs/agents/mcp-agent.d.ts +10 -4
  10. package/lib/cjs/agents/mcp-agent.js +12 -6
  11. package/lib/cjs/agents/memory.d.ts +1 -1
  12. package/lib/cjs/agents/team-agent.d.ts +28 -0
  13. package/lib/cjs/agents/team-agent.js +93 -0
  14. package/lib/cjs/agents/user-agent.d.ts +9 -10
  15. package/lib/cjs/agents/user-agent.js +10 -13
  16. package/lib/{esm/execution-engine/execution-engine.d.ts → cjs/aigne/aigne.d.ts} +9 -12
  17. package/lib/cjs/{execution-engine/execution-engine.js → aigne/aigne.js} +19 -19
  18. package/lib/cjs/{execution-engine → aigne}/context.d.ts +31 -32
  19. package/lib/cjs/{execution-engine → aigne}/context.js +30 -40
  20. package/lib/cjs/aigne/index.d.ts +4 -0
  21. package/lib/cjs/{execution-engine → aigne}/index.js +2 -2
  22. package/lib/cjs/{execution-engine → aigne}/usage.d.ts +1 -1
  23. package/lib/cjs/client/client.d.ts +19 -0
  24. package/lib/cjs/client/client.js +49 -0
  25. package/lib/cjs/index.d.ts +2 -1
  26. package/lib/cjs/index.js +2 -1
  27. package/lib/cjs/loader/agent-js.d.ts +2 -2
  28. package/lib/cjs/loader/agent-js.js +4 -5
  29. package/lib/cjs/loader/agent-yaml.d.ts +8 -5
  30. package/lib/cjs/loader/agent-yaml.js +21 -2
  31. package/lib/cjs/loader/index.d.ts +5 -5
  32. package/lib/cjs/loader/index.js +8 -19
  33. package/lib/cjs/models/chat-model.d.ts +1 -1
  34. package/lib/cjs/models/claude-chat-model.d.ts +3 -1
  35. package/lib/cjs/models/claude-chat-model.js +75 -60
  36. package/lib/cjs/models/openai-chat-model.d.ts +3 -3
  37. package/lib/cjs/models/openai-chat-model.js +1 -3
  38. package/lib/cjs/prompt/prompt-builder.d.ts +1 -1
  39. package/lib/cjs/prompt/prompt-builder.js +3 -3
  40. package/lib/cjs/server/error.d.ts +4 -0
  41. package/lib/cjs/server/error.js +11 -0
  42. package/lib/cjs/server/server.d.ts +54 -0
  43. package/lib/cjs/server/server.js +130 -0
  44. package/lib/cjs/utils/camelize.d.ts +13 -0
  45. package/lib/cjs/utils/camelize.js +16 -0
  46. package/lib/cjs/utils/event-stream.d.ts +11 -0
  47. package/lib/cjs/utils/event-stream.js +91 -0
  48. package/lib/cjs/utils/mcp-utils.js +4 -1
  49. package/lib/cjs/utils/stream-utils.d.ts +10 -3
  50. package/lib/cjs/utils/stream-utils.js +51 -36
  51. package/lib/cjs/utils/type-utils.d.ts +4 -2
  52. package/lib/cjs/utils/type-utils.js +10 -2
  53. package/lib/dts/agents/agent.d.ts +21 -20
  54. package/lib/dts/agents/ai-agent.d.ts +9 -8
  55. package/lib/dts/agents/mcp-agent.d.ts +10 -4
  56. package/lib/dts/agents/memory.d.ts +1 -1
  57. package/lib/dts/agents/team-agent.d.ts +28 -0
  58. package/lib/dts/agents/user-agent.d.ts +9 -10
  59. package/lib/dts/{execution-engine/execution-engine.d.ts → aigne/aigne.d.ts} +9 -12
  60. package/lib/dts/{execution-engine → aigne}/context.d.ts +31 -32
  61. package/lib/dts/aigne/index.d.ts +4 -0
  62. package/lib/dts/{execution-engine → aigne}/usage.d.ts +1 -1
  63. package/lib/dts/client/client.d.ts +19 -0
  64. package/lib/dts/index.d.ts +2 -1
  65. package/lib/dts/loader/agent-js.d.ts +2 -2
  66. package/lib/dts/loader/agent-yaml.d.ts +8 -5
  67. package/lib/dts/loader/index.d.ts +5 -5
  68. package/lib/dts/models/chat-model.d.ts +1 -1
  69. package/lib/dts/models/claude-chat-model.d.ts +3 -1
  70. package/lib/dts/models/openai-chat-model.d.ts +3 -3
  71. package/lib/dts/prompt/prompt-builder.d.ts +1 -1
  72. package/lib/dts/server/error.d.ts +4 -0
  73. package/lib/dts/server/server.d.ts +54 -0
  74. package/lib/dts/utils/camelize.d.ts +13 -0
  75. package/lib/dts/utils/event-stream.d.ts +11 -0
  76. package/lib/dts/utils/stream-utils.d.ts +10 -3
  77. package/lib/dts/utils/type-utils.d.ts +4 -2
  78. package/lib/esm/agents/agent.d.ts +21 -20
  79. package/lib/esm/agents/agent.js +29 -27
  80. package/lib/esm/agents/ai-agent.d.ts +9 -8
  81. package/lib/esm/agents/ai-agent.js +20 -14
  82. package/lib/esm/agents/mcp-agent.d.ts +10 -4
  83. package/lib/esm/agents/mcp-agent.js +12 -6
  84. package/lib/esm/agents/memory.d.ts +1 -1
  85. package/lib/esm/agents/team-agent.d.ts +28 -0
  86. package/lib/esm/agents/team-agent.js +89 -0
  87. package/lib/esm/agents/user-agent.d.ts +9 -10
  88. package/lib/esm/agents/user-agent.js +11 -14
  89. package/lib/{cjs/execution-engine/execution-engine.d.ts → esm/aigne/aigne.d.ts} +9 -12
  90. package/lib/esm/{execution-engine/execution-engine.js → aigne/aigne.js} +18 -18
  91. package/lib/esm/{execution-engine → aigne}/context.d.ts +31 -32
  92. package/lib/esm/{execution-engine → aigne}/context.js +28 -38
  93. package/lib/esm/aigne/index.d.ts +4 -0
  94. package/lib/esm/aigne/index.js +4 -0
  95. package/lib/esm/{execution-engine → aigne}/usage.d.ts +1 -1
  96. package/lib/esm/client/client.d.ts +19 -0
  97. package/lib/esm/client/client.js +45 -0
  98. package/lib/esm/index.d.ts +2 -1
  99. package/lib/esm/index.js +2 -1
  100. package/lib/esm/loader/agent-js.d.ts +2 -2
  101. package/lib/esm/loader/agent-js.js +4 -5
  102. package/lib/esm/loader/agent-yaml.d.ts +8 -5
  103. package/lib/esm/loader/agent-yaml.js +21 -2
  104. package/lib/esm/loader/index.d.ts +5 -5
  105. package/lib/esm/loader/index.js +8 -19
  106. package/lib/esm/models/chat-model.d.ts +1 -1
  107. package/lib/esm/models/claude-chat-model.d.ts +3 -1
  108. package/lib/esm/models/claude-chat-model.js +75 -60
  109. package/lib/esm/models/openai-chat-model.d.ts +3 -3
  110. package/lib/esm/models/openai-chat-model.js +1 -3
  111. package/lib/esm/prompt/prompt-builder.d.ts +1 -1
  112. package/lib/esm/prompt/prompt-builder.js +3 -3
  113. package/lib/esm/server/error.d.ts +4 -0
  114. package/lib/esm/server/error.js +7 -0
  115. package/lib/esm/server/server.d.ts +54 -0
  116. package/lib/esm/server/server.js +123 -0
  117. package/lib/esm/utils/camelize.d.ts +13 -0
  118. package/lib/esm/utils/camelize.js +10 -0
  119. package/lib/esm/utils/event-stream.d.ts +11 -0
  120. package/lib/esm/utils/event-stream.js +85 -0
  121. package/lib/esm/utils/mcp-utils.js +4 -1
  122. package/lib/esm/utils/stream-utils.d.ts +10 -3
  123. package/lib/esm/utils/stream-utils.js +49 -35
  124. package/lib/esm/utils/type-utils.d.ts +4 -2
  125. package/lib/esm/utils/type-utils.js +9 -2
  126. package/package.json +15 -5
  127. package/lib/cjs/execution-engine/index.d.ts +0 -4
  128. package/lib/cjs/execution-engine/utils.d.ts +0 -4
  129. package/lib/cjs/execution-engine/utils.js +0 -34
  130. package/lib/dts/execution-engine/index.d.ts +0 -4
  131. package/lib/dts/execution-engine/utils.d.ts +0 -4
  132. package/lib/esm/execution-engine/index.d.ts +0 -4
  133. package/lib/esm/execution-engine/index.js +0 -4
  134. package/lib/esm/execution-engine/utils.d.ts +0 -4
  135. package/lib/esm/execution-engine/utils.js +0 -30
  136. /package/lib/cjs/{execution-engine → aigne}/message-queue.d.ts +0 -0
  137. /package/lib/cjs/{execution-engine → aigne}/message-queue.js +0 -0
  138. /package/lib/cjs/{execution-engine → aigne}/usage.js +0 -0
  139. /package/lib/dts/{execution-engine → aigne}/message-queue.d.ts +0 -0
  140. /package/lib/esm/{execution-engine → aigne}/message-queue.d.ts +0 -0
  141. /package/lib/esm/{execution-engine → aigne}/message-queue.js +0 -0
  142. /package/lib/esm/{execution-engine → aigne}/usage.js +0 -0
@@ -3,7 +3,7 @@ import { ZodObject, z } from "zod";
3
3
  import { createMessage } from "../prompt/prompt-builder.js";
4
4
  import { logger } from "../utils/logger.js";
5
5
  import { agentResponseStreamToObject, asyncGeneratorToReadableStream, isAsyncGenerator, objectToAgentResponseStream, onAgentResponseStreamEnd, } from "../utils/stream-utils.js";
6
- import { checkArguments, createAccessorArray, orArrayToArray, } from "../utils/type-utils.js";
6
+ import { checkArguments, createAccessorArray, isEmpty, orArrayToArray, } from "../utils/type-utils.js";
7
7
  import { AgentMemory } from "./memory.js";
8
8
  import { replaceTransferAgentToName, transferToAgentOutput, } from "./types.js";
9
9
  export class Agent {
@@ -19,8 +19,8 @@ export class Agent {
19
19
  this.includeInputInOutput = options.includeInputInOutput;
20
20
  this.subscribeTopic = options.subscribeTopic;
21
21
  this.publishTopic = options.publishTopic;
22
- if (options.tools?.length)
23
- this.tools.push(...options.tools.map(functionToAgent));
22
+ if (options.skills?.length)
23
+ this.skills.push(...options.skills.map(functionToAgent));
24
24
  this.disableEvents = options.disableEvents;
25
25
  if (options.memory) {
26
26
  this.memory =
@@ -57,11 +57,11 @@ export class Agent {
57
57
  includeInputInOutput;
58
58
  subscribeTopic;
59
59
  publishTopic;
60
- tools = createAccessorArray([], (arr, name) => arr.find((t) => t.name === name));
60
+ skills = createAccessorArray([], (arr, name) => arr.find((t) => t.name === name));
61
61
  disableEvents;
62
62
  /**
63
63
  * Attach agent to context:
64
- * - subscribe to topic and call process method when message received
64
+ * - subscribe to topic and invoke process method when message received
65
65
  * - subscribe to memory topic if memory is enabled
66
66
  * @param context Context to attach
67
67
  */
@@ -70,7 +70,7 @@ export class Agent {
70
70
  for (const topic of orArrayToArray(this.subscribeTopic).concat(this.topic)) {
71
71
  context.subscribe(topic, async ({ message, context }) => {
72
72
  try {
73
- await context.call(this, message);
73
+ await context.invoke(this, message);
74
74
  }
75
75
  catch (error) {
76
76
  context.emit("agentFailed", { agent: this, error });
@@ -78,34 +78,37 @@ export class Agent {
78
78
  });
79
79
  }
80
80
  }
81
- addTool(tool) {
82
- this.tools.push(typeof tool === "function" ? functionToAgent(tool) : tool);
81
+ addSkill(...skills) {
82
+ this.skills.push(...skills.map((skill) => (typeof skill === "function" ? functionToAgent(skill) : skill)));
83
83
  }
84
- get isCallable() {
84
+ get isInvokable() {
85
85
  return !!this.process;
86
86
  }
87
87
  checkContextStatus(context) {
88
88
  if (context) {
89
89
  const { status } = context;
90
90
  if (status === "timeout") {
91
- throw new Error(`ExecutionEngine for agent ${this.name} has timed out`);
91
+ throw new Error(`AIGNE for agent ${this.name} has timed out`);
92
92
  }
93
93
  }
94
94
  }
95
95
  async newDefaultContext() {
96
- return import("../execution-engine/context.js").then((m) => new m.ExecutionContext());
96
+ return import("../aigne/context.js").then((m) => new m.AIGNEContext());
97
97
  }
98
- async call(input, context, options) {
98
+ async invoke(input, context, options) {
99
99
  const ctx = context ?? (await this.newDefaultContext());
100
100
  const message = typeof input === "string" ? createMessage(input) : input;
101
- logger.core("Call agent %s started with input: %O", this.name, input);
101
+ logger.core("Invoke agent %s started with input: %O", this.name, input);
102
102
  if (!this.disableEvents)
103
103
  ctx.emit("agentStarted", { agent: this, input: message });
104
104
  try {
105
105
  const parsedInput = checkArguments(`Agent ${this.name} input`, this.inputSchema, message);
106
106
  this.preprocess(parsedInput, ctx);
107
107
  this.checkContextStatus(ctx);
108
- const response = await this.process(parsedInput, ctx, options);
108
+ let response = await this.process(parsedInput, ctx, options);
109
+ if (response instanceof Agent) {
110
+ response = transferToAgentOutput(response);
111
+ }
109
112
  if (options?.streaming) {
110
113
  const stream = response instanceof ReadableStream
111
114
  ? response
@@ -139,27 +142,27 @@ export class Agent {
139
142
  const parsedOutput = checkArguments(`Agent ${this.name} output`, this.outputSchema, output);
140
143
  const finalOutput = this.includeInputInOutput ? { ...input, ...parsedOutput } : parsedOutput;
141
144
  this.postprocess(input, finalOutput, context);
142
- logger.core("Call agent %s succeed with output: %O", this.name, finalOutput);
145
+ logger.core("Invoke agent %s succeed with output: %O", this.name, finalOutput);
143
146
  if (!this.disableEvents)
144
147
  context.emit("agentSucceed", { agent: this, output: finalOutput });
145
148
  return finalOutput;
146
149
  }
147
150
  processAgentError(error, context) {
148
- logger.core("Call agent %s failed with error: %O", this.name, error);
151
+ logger.core("Invoke agent %s failed with error: %O", this.name, error);
149
152
  if (!this.disableEvents)
150
153
  context.emit("agentFailed", { agent: this, error });
151
154
  throw error;
152
155
  }
153
- checkUsageAgentCalls(context) {
156
+ checkAgentInvokesUsage(context) {
154
157
  const { limits, usage } = context;
155
- if (limits?.maxAgentCalls && usage.agentCalls >= limits.maxAgentCalls) {
156
- throw new Error(`Exceeded max agent calls ${usage.agentCalls}/${limits.maxAgentCalls}`);
158
+ if (limits?.maxAgentInvokes && usage.agentCalls >= limits.maxAgentInvokes) {
159
+ throw new Error(`Exceeded max agent invokes ${usage.agentCalls}/${limits.maxAgentInvokes}`);
157
160
  }
158
161
  usage.agentCalls++;
159
162
  }
160
163
  preprocess(_, context) {
161
164
  this.checkContextStatus(context);
162
- this.checkUsageAgentCalls(context);
165
+ this.checkAgentInvokesUsage(context);
163
166
  }
164
167
  postprocess(input, output, context) {
165
168
  this.checkContextStatus(context);
@@ -177,9 +180,12 @@ export class Agent {
177
180
  return this.name;
178
181
  }
179
182
  }
183
+ export function isEmptyChunk(chunk) {
184
+ return isEmpty(chunk.delta.json) && isEmpty(chunk.delta.text);
185
+ }
180
186
  function checkAgentInputOutputSchema(schema) {
181
187
  if (!(schema instanceof ZodObject) && typeof schema !== "function") {
182
- throw new Error("schema must be a zod object or function return a zod object ");
188
+ throw new Error(`schema must be a zod object or function return a zod object, got: ${typeof schema}`);
183
189
  }
184
190
  }
185
191
  export class FunctionAgent extends Agent {
@@ -191,12 +197,8 @@ export class FunctionAgent extends Agent {
191
197
  this.fn = options.fn ?? (() => ({}));
192
198
  }
193
199
  fn;
194
- async process(input, context, options) {
195
- let result = await this.fn(input, context);
196
- if (result instanceof Agent) {
197
- result = transferToAgentOutput(result);
198
- }
199
- return options?.streaming ? objectToAgentResponseStream(result) : result;
200
+ process(input, context) {
201
+ return this.fn(input, context);
200
202
  }
201
203
  }
202
204
  function functionToAgent(agent) {
@@ -1,9 +1,9 @@
1
1
  import { z } from "zod";
2
- import type { Context } from "../execution-engine/context.js";
2
+ import type { Context } from "../aigne/context.js";
3
3
  import { ChatModel } from "../models/chat-model.js";
4
+ import type { ChatModelInput } from "../models/chat-model.js";
4
5
  import { PromptBuilder } from "../prompt/prompt-builder.js";
5
6
  import { Agent, type AgentOptions, type AgentProcessAsyncGenerator, type Message } from "./agent.js";
6
- import { type TransferAgentOutput } from "./types.js";
7
7
  export interface AIAgentOptions<I extends Message = Message, O extends Message = Message> extends AgentOptions<I, O> {
8
8
  model?: ChatModel;
9
9
  instructions?: string | PromptBuilder;
@@ -24,18 +24,18 @@ export declare const aiAgentOptionsSchema: z.ZodObject<{
24
24
  publishTopic: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">, z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>]>>;
25
25
  name: z.ZodOptional<z.ZodString>;
26
26
  description: z.ZodOptional<z.ZodString>;
27
- tools: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodType<Agent<Message, Message>, z.ZodTypeDef, Agent<Message, Message>>, z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>]>, "many">>;
27
+ skills: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodType<Agent<Message, Message>, z.ZodTypeDef, Agent<Message, Message>>, z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>]>, "many">>;
28
28
  disableLogging: z.ZodOptional<z.ZodBoolean>;
29
29
  memory: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodAny, z.ZodAny]>>;
30
30
  }, "strip", z.ZodTypeAny, {
31
31
  description?: string | undefined;
32
32
  memory?: any;
33
+ name?: string | undefined;
33
34
  includeInputInOutput?: boolean | undefined;
34
35
  subscribeTopic?: string | string[] | undefined;
35
36
  publishTopic?: string | string[] | ((...args: unknown[]) => unknown) | undefined;
36
- tools?: (Agent<Message, Message> | ((...args: unknown[]) => unknown))[] | undefined;
37
+ skills?: (Agent<Message, Message> | ((...args: unknown[]) => unknown))[] | undefined;
37
38
  toolChoice?: Agent<Message, Message> | "auto" | "none" | "required" | "router" | undefined;
38
- name?: string | undefined;
39
39
  model?: ChatModel | undefined;
40
40
  instructions?: string | PromptBuilder | undefined;
41
41
  outputKey?: string | undefined;
@@ -45,12 +45,12 @@ export declare const aiAgentOptionsSchema: z.ZodObject<{
45
45
  }, {
46
46
  description?: string | undefined;
47
47
  memory?: any;
48
+ name?: string | undefined;
48
49
  includeInputInOutput?: boolean | undefined;
49
50
  subscribeTopic?: string | string[] | undefined;
50
51
  publishTopic?: string | string[] | ((...args: unknown[]) => unknown) | undefined;
51
- tools?: (Agent<Message, Message> | ((...args: unknown[]) => unknown))[] | undefined;
52
+ skills?: (Agent<Message, Message> | ((...args: unknown[]) => unknown))[] | undefined;
52
53
  toolChoice?: Agent<Message, Message> | "auto" | "none" | "required" | "router" | undefined;
53
- name?: string | undefined;
54
54
  model?: ChatModel | undefined;
55
55
  instructions?: string | PromptBuilder | undefined;
56
56
  outputKey?: string | undefined;
@@ -65,5 +65,6 @@ export declare class AIAgent<I extends Message = Message, O extends Message = Me
65
65
  instructions: PromptBuilder;
66
66
  outputKey?: string;
67
67
  toolChoice?: AIAgentToolChoice;
68
- process(input: I, context: Context): AgentProcessAsyncGenerator<O | TransferAgentOutput>;
68
+ process(input: I, context: Context): AgentProcessAsyncGenerator<O>;
69
+ processRouter(input: I, model: ChatModel, modelInput: ChatModelInput, context: Context, toolsMap: Map<string, Agent>): AgentProcessAsyncGenerator<O>;
69
70
  }
@@ -25,7 +25,7 @@ export const aiAgentOptionsSchema = z.object({
25
25
  publishTopic: z.union([z.string(), z.array(z.string()), z.function()]).optional(),
26
26
  name: z.string().optional(),
27
27
  description: z.string().optional(),
28
- tools: z.array(z.union([z.instanceof(Agent), z.function()])).optional(),
28
+ skills: z.array(z.union([z.instanceof(Agent), z.function()])).optional(),
29
29
  disableLogging: z.boolean().optional(),
30
30
  memory: z.union([z.boolean(), z.any(), z.any()]).optional(),
31
31
  });
@@ -49,21 +49,25 @@ export class AIAgent extends Agent {
49
49
  outputKey;
50
50
  toolChoice;
51
51
  async *process(input, context) {
52
- const model = context.model ?? this.model;
52
+ const model = this.model ?? context.model;
53
53
  if (!model)
54
54
  throw new Error("model is required to run AIAgent");
55
- const { toolAgents, messages, ...modelInput } = await this.instructions.build({
55
+ const { toolAgents, ...modelInput } = await this.instructions.build({
56
56
  agent: this,
57
57
  input,
58
58
  model,
59
59
  context,
60
60
  });
61
61
  const toolsMap = new Map(toolAgents?.map((i) => [i.name, i]));
62
+ if (this.toolChoice === "router") {
63
+ yield* this.processRouter(input, model, modelInput, context, toolsMap);
64
+ return;
65
+ }
62
66
  const toolCallMessages = [];
63
67
  const outputKey = this.outputKey || MESSAGE_KEY;
64
68
  for (;;) {
65
69
  const modelOutput = {};
66
- const stream = await context.call(model, { ...modelInput, messages: messages.concat(toolCallMessages) }, { streaming: true });
70
+ const stream = await context.invoke(model, { ...modelInput, messages: modelInput.messages.concat(toolCallMessages) }, { streaming: true });
67
71
  for await (const value of readableStreamToAsyncIterator(stream)) {
68
72
  if (value.delta.text?.text) {
69
73
  yield { delta: { text: { [outputKey]: value.delta.text.text } } };
@@ -81,7 +85,7 @@ export class AIAgent extends Agent {
81
85
  if (!tool)
82
86
  throw new Error(`Tool not found: ${call.function.name}`);
83
87
  // NOTE: should pass both arguments (model generated) and input (user provided) to the tool
84
- const output = await context.call(tool, { ...call.function.arguments, ...input }, { disableTransfer: true });
88
+ const output = await context.invoke(tool, { ...call.function.arguments, ...input }, { disableTransfer: true });
85
89
  // NOTE: Return transfer output immediately
86
90
  if (isTransferAgentOutput(output)) {
87
91
  return output;
@@ -91,15 +95,6 @@ export class AIAgent extends Agent {
91
95
  // Continue LLM function calling loop if any tools were executed
92
96
  if (executedToolCalls.length) {
93
97
  toolCallMessages.push(AgentMessageTemplate.from(undefined, executedToolCalls.map(({ call }) => call)).format(), ...executedToolCalls.map(({ call, output }) => ToolMessageTemplate.from(output, call.id).format()));
94
- // Return the output of the first tool if the toolChoice is "router"
95
- if (this.toolChoice === "router") {
96
- const output = executedToolCalls[0]?.output;
97
- const { supportsParallelToolCalls } = model.getModelCapabilities();
98
- if (!output || (supportsParallelToolCalls && executedToolCalls.length !== 1)) {
99
- throw new Error("Router toolChoice requires exactly one tool to be executed");
100
- }
101
- return output;
102
- }
103
98
  continue;
104
99
  }
105
100
  }
@@ -116,4 +111,15 @@ export class AIAgent extends Agent {
116
111
  return;
117
112
  }
118
113
  }
114
+ async *processRouter(input, model, modelInput, context, toolsMap) {
115
+ const { toolCalls: [call] = [], } = await context.invoke(model, modelInput);
116
+ if (!call) {
117
+ throw new Error("Router toolChoice requires exactly one tool to be executed");
118
+ }
119
+ const tool = toolsMap.get(call.function.name);
120
+ if (!tool)
121
+ throw new Error(`Tool not found: ${call.function.name}`);
122
+ const stream = await context.invoke(tool, { ...call.function.arguments, ...input }, { streaming: true });
123
+ yield* readableStreamToAsyncIterator(stream);
124
+ }
119
125
  }
@@ -1,11 +1,12 @@
1
1
  import { Client, type ClientOptions } from "@modelcontextprotocol/sdk/client/index.js";
2
2
  import { type SSEClientTransportOptions } from "@modelcontextprotocol/sdk/client/sse.js";
3
3
  import { type StdioServerParameters } from "@modelcontextprotocol/sdk/client/stdio.js";
4
+ import { type StreamableHTTPClientTransportOptions } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
4
5
  import type { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
5
6
  import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
6
7
  import type { CallToolResult, GetPromptResult, Implementation, ReadResourceResult, Request } from "@modelcontextprotocol/sdk/types.js";
7
8
  import { type ZodType, z } from "zod";
8
- import type { Context } from "../execution-engine/context.js";
9
+ import type { Context } from "../aigne/context.js";
9
10
  import { type PromiseOrValue } from "../utils/type-utils.js";
10
11
  import { Agent, type AgentOptions, type Message } from "./agent.js";
11
12
  export interface MCPAgentOptions extends AgentOptions {
@@ -17,9 +18,14 @@ export type MCPServerOptions = SSEServerParameters | StdioServerParameters;
17
18
  export type SSEServerParameters = {
18
19
  url: string;
19
20
  /**
20
- * Additional options to pass to the SSEClientTransport.
21
+ * Whether to use the StreamableHTTPClientTransport instead of the SSEClientTransport.
22
+ * @default "sse"
21
23
  */
22
- opts?: SSEClientTransportOptions;
24
+ transport?: "sse" | "streamableHttp";
25
+ /**
26
+ * Additional options to pass to the SSEClientTransport or StreamableHTTPClientTransport.
27
+ */
28
+ opts?: SSEClientTransportOptions | StreamableHTTPClientTransportOptions;
23
29
  /**
24
30
  * The timeout for requests to the server, in milliseconds.
25
31
  * @default 60000
@@ -48,7 +54,7 @@ export declare class MCPAgent extends Agent {
48
54
  readonly resources: MCPResource[] & {
49
55
  [key: string]: MCPResource;
50
56
  };
51
- get isCallable(): boolean;
57
+ get isInvokable(): boolean;
52
58
  process(_input: Message, _context?: Context): Promise<Message>;
53
59
  shutdown(): Promise<void>;
54
60
  }
@@ -1,6 +1,7 @@
1
1
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
2
  import { SSEClientTransport, } from "@modelcontextprotocol/sdk/client/sse.js";
3
3
  import { StdioClientTransport, getDefaultEnvironment, } from "@modelcontextprotocol/sdk/client/stdio.js";
4
+ import { StreamableHTTPClientTransport, } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
4
5
  import { UriTemplate } from "@modelcontextprotocol/sdk/shared/uriTemplate.js";
5
6
  import pRetry from "p-retry";
6
7
  import { z } from "zod";
@@ -8,8 +9,8 @@ import { logger } from "../utils/logger.js";
8
9
  import { promptFromMCPPrompt, resourceFromMCPResource, toolFromMCPTool, } from "../utils/mcp-utils.js";
9
10
  import { checkArguments, createAccessorArray } from "../utils/type-utils.js";
10
11
  import { Agent } from "./agent.js";
11
- const MCP_AGENT_CLIENT_NAME = "MCPAgent";
12
- const MCP_AGENT_CLIENT_VERSION = "0.0.1";
12
+ const MCP_AGENT_CLIENT_NAME = "AIGNE/MCPAgent";
13
+ const MCP_AGENT_CLIENT_VERSION = "1.10.0"; // This should match the version in package.json
13
14
  const DEFAULT_MAX_RECONNECTS = 10;
14
15
  const DEFAULT_TIMEOUT = () => z.coerce
15
16
  .number()
@@ -35,7 +36,12 @@ export class MCPAgent extends Agent {
35
36
  static from(options) {
36
37
  checkArguments("MCPAgent.from", mcpAgentOptionsSchema, options);
37
38
  if (isSSEServerParameters(options)) {
38
- const transport = () => new SSEClientTransport(new URL(options.url), options.opts);
39
+ const transport = () => {
40
+ if (options.transport === "streamableHttp") {
41
+ return new StreamableHTTPClientTransport(new URL(options.url), options.opts);
42
+ }
43
+ return new SSEClientTransport(new URL(options.url), options.opts);
44
+ };
39
45
  return MCPAgent.fromTransport(transport, options);
40
46
  }
41
47
  if (isStdioServerParameters(options)) {
@@ -62,7 +68,7 @@ export class MCPAgent extends Agent {
62
68
  const mcpServer = getMCPServerName(client);
63
69
  const { tools: isToolsAvailable, prompts: isPromptsAvailable, resources: isResourcesAvailable, } = client.getServerCapabilities() ?? {};
64
70
  logger.mcp(`Listing tools from ${mcpServer}`);
65
- const tools = isToolsAvailable
71
+ const skills = isToolsAvailable
66
72
  ? await client.listTools().then(({ tools }) => {
67
73
  logger.mcp(`Listing tools from ${mcpServer} completed %O`, tools?.map((i) => i.name));
68
74
  return tools.map((tool) => toolFromMCPTool(tool, { client }));
@@ -91,7 +97,7 @@ export class MCPAgent extends Agent {
91
97
  return new MCPAgent({
92
98
  name: client.getServerVersion()?.name,
93
99
  client,
94
- tools,
100
+ skills,
95
101
  prompts,
96
102
  resources,
97
103
  });
@@ -107,7 +113,7 @@ export class MCPAgent extends Agent {
107
113
  client;
108
114
  prompts = createAccessorArray([], (arr, name) => arr.find((i) => i.name === name));
109
115
  resources = createAccessorArray([], (arr, name) => arr.find((i) => i.name === name));
110
- get isCallable() {
116
+ get isInvokable() {
111
117
  return false;
112
118
  }
113
119
  async process(_input, _context) {
@@ -1,4 +1,4 @@
1
- import type { Context } from "../execution-engine/context.js";
1
+ import type { Context } from "../aigne/context.js";
2
2
  import type { Message } from "./agent.js";
3
3
  export interface AgentMemoryOptions {
4
4
  /**
@@ -0,0 +1,28 @@
1
+ import type { Context } from "../aigne/context.js";
2
+ import { type PromiseOrValue } from "../utils/type-utils.js";
3
+ import { Agent, type AgentOptions, type AgentProcessResult, type Message } from "./agent.js";
4
+ export declare enum ProcessMode {
5
+ /**
6
+ * Process the agents one by one, passing the output of each agent to the next.
7
+ */
8
+ sequential = "sequential",
9
+ /**
10
+ * Process all agents in parallel, merging the output of all agents.
11
+ */
12
+ parallel = "parallel"
13
+ }
14
+ export interface TeamAgentOptions<I extends Message, O extends Message> extends AgentOptions<I, O> {
15
+ /**
16
+ * The method to process the agents in the team.
17
+ * @default {ProcessMode.sequential}
18
+ */
19
+ mode?: ProcessMode;
20
+ }
21
+ export declare class TeamAgent<I extends Message, O extends Message> extends Agent<I, O> {
22
+ static from<I extends Message, O extends Message>(options: TeamAgentOptions<I, O>): TeamAgent<I, O>;
23
+ constructor(options: TeamAgentOptions<I, O>);
24
+ mode: ProcessMode;
25
+ process(input: I, context: Context): PromiseOrValue<AgentProcessResult<O>>;
26
+ _processSequential(input: I, context: Context): PromiseOrValue<AgentProcessResult<O>>;
27
+ _processParallel(input: I, context: Context): PromiseOrValue<AgentProcessResult<O>>;
28
+ }
@@ -0,0 +1,89 @@
1
+ import { mergeAgentResponseChunk, readableStreamToAsyncIterator } from "../utils/stream-utils.js";
2
+ import { isEmpty } from "../utils/type-utils.js";
3
+ import { Agent, } from "./agent.js";
4
+ export var ProcessMode;
5
+ (function (ProcessMode) {
6
+ /**
7
+ * Process the agents one by one, passing the output of each agent to the next.
8
+ */
9
+ ProcessMode["sequential"] = "sequential";
10
+ /**
11
+ * Process all agents in parallel, merging the output of all agents.
12
+ */
13
+ ProcessMode["parallel"] = "parallel";
14
+ })(ProcessMode || (ProcessMode = {}));
15
+ export class TeamAgent extends Agent {
16
+ static from(options) {
17
+ return new TeamAgent(options);
18
+ }
19
+ constructor(options) {
20
+ super(options);
21
+ this.mode = options.mode ?? ProcessMode.sequential;
22
+ }
23
+ mode;
24
+ process(input, context) {
25
+ switch (this.mode) {
26
+ case ProcessMode.sequential:
27
+ return this._processSequential(input, context);
28
+ case ProcessMode.parallel:
29
+ return this._processParallel(input, context);
30
+ }
31
+ }
32
+ async *_processSequential(input, context) {
33
+ const output = {};
34
+ // Clone the agents to run, so that we can update the agents list during the loop
35
+ const agents = [...this.skills];
36
+ const newAgents = [];
37
+ for (const agent of agents) {
38
+ const [o, transferToAgent] = await context.invoke(agent, { ...input, ...output }, { returnActiveAgent: true, streaming: true });
39
+ for await (const chunk of readableStreamToAsyncIterator(o)) {
40
+ yield chunk;
41
+ mergeAgentResponseChunk(output, chunk);
42
+ }
43
+ newAgents.push(await transferToAgent);
44
+ }
45
+ this.skills.splice(0);
46
+ this.skills.push(...newAgents);
47
+ }
48
+ async *_processParallel(input, context) {
49
+ const result = await Promise.all(this.skills.map((agent) => context.invoke(agent, input, { returnActiveAgent: true, streaming: true })));
50
+ const streams = result.map((i) => i[0]);
51
+ const read = async (index, reader) => {
52
+ const promise = reader.read();
53
+ return promise.then((result) => ({ ...result, reader, index }));
54
+ };
55
+ const tasks = new Map(streams.map((stream, index) => [index, read(index, stream.getReader())]));
56
+ // NOTE: Flag to check if the output key is used by agent at the index,
57
+ const outputKeyUsed = new Map();
58
+ while (tasks.size) {
59
+ const { value, done, reader, index } = await Promise.race(tasks.values());
60
+ tasks.delete(index);
61
+ if (!done) {
62
+ tasks.set(index, read(index, reader));
63
+ }
64
+ if (value) {
65
+ let { delta: { text, ...delta }, } = value;
66
+ if (text) {
67
+ for (const key of Object.keys(text)) {
68
+ // the output key is unused, add to map to lock it
69
+ if (!outputKeyUsed.has(key)) {
70
+ outputKeyUsed.set(key, index);
71
+ }
72
+ // the output key is used by the agent at the index, abandon it
73
+ else if (outputKeyUsed.get(key) !== index) {
74
+ delete text[key];
75
+ }
76
+ }
77
+ if (isEmpty(text)) {
78
+ text = undefined;
79
+ }
80
+ }
81
+ if (!isEmpty(delta.json) || !isEmpty(text))
82
+ yield { delta: { ...delta, text } };
83
+ }
84
+ }
85
+ const agents = await Promise.all(result.map((i) => i[1]));
86
+ this.skills.splice(0);
87
+ this.skills.push(...agents);
88
+ }
89
+ }
@@ -1,26 +1,25 @@
1
1
  import { ReadableStream } from "node:stream/web";
2
- import { type Context, type Runnable } from "../execution-engine/context.js";
3
- import type { MessagePayload } from "../execution-engine/message-queue.js";
4
- import { type PromiseOrValue } from "../utils/type-utils.js";
5
- import { Agent, type AgentOptions, type AgentProcessAsyncGenerator, type Message } from "./agent.js";
2
+ import { type Context } from "../aigne/context.js";
3
+ import type { MessagePayload } from "../aigne/message-queue.js";
4
+ import { type Agent, type AgentOptions, type AgentProcessResult, FunctionAgent, type FunctionAgentFn, type Message } from "./agent.js";
6
5
  export interface UserAgentOptions<I extends Message = Message, O extends Message = Message> extends AgentOptions<I, O> {
7
6
  context: Context;
8
- process?: (input: I, context: Context) => PromiseOrValue<O>;
9
- activeAgent?: Runnable;
7
+ process?: FunctionAgentFn<I, O>;
8
+ activeAgent?: Agent;
10
9
  }
11
- export declare class UserAgent<I extends Message = Message, O extends Message = Message> extends Agent<I, O> {
10
+ export declare class UserAgent<I extends Message = Message, O extends Message = Message> extends FunctionAgent<I, O> {
12
11
  static from<I extends Message, O extends Message>(options: UserAgentOptions<I, O>): UserAgent<I, O>;
13
12
  constructor(options: UserAgentOptions<I, O>);
14
13
  context: Context;
15
14
  private _process?;
16
15
  private activeAgent?;
17
- call: Agent<I, O>["call"];
18
- process(input: I, context: Context): AgentProcessAsyncGenerator<O>;
16
+ invoke: Agent<I, O>["invoke"];
17
+ process(input: I, context: Context): Promise<AgentProcessResult<O>>;
19
18
  publish: Context["publish"];
20
19
  subscribe: Context["subscribe"];
21
20
  unsubscribe: Context["unsubscribe"];
22
21
  get stream(): ReadableStream<MessagePayload & {
23
22
  topic: string;
24
23
  }>;
25
- protected checkUsageAgentCalls(_context: Context): void;
24
+ protected checkAgentInvokesUsage(_context: Context): void;
26
25
  }
@@ -1,9 +1,8 @@
1
1
  import { ReadableStream } from "node:stream/web";
2
- import { createPublishMessage } from "../execution-engine/context.js";
3
- import { readableStreamToAsyncIterator } from "../utils/stream-utils.js";
2
+ import { createPublishMessage } from "../aigne/context.js";
4
3
  import { orArrayToArray } from "../utils/type-utils.js";
5
- import { Agent, } from "./agent.js";
6
- export class UserAgent extends Agent {
4
+ import { FunctionAgent, } from "./agent.js";
5
+ export class UserAgent extends FunctionAgent {
7
6
  static from(options) {
8
7
  return new UserAgent(options);
9
8
  }
@@ -16,31 +15,29 @@ export class UserAgent extends Agent {
16
15
  context;
17
16
  _process;
18
17
  activeAgent;
19
- call = ((input, context, options) => {
18
+ invoke = ((input, context, options) => {
20
19
  if (!context)
21
20
  this.context = this.context.newContext({ reset: true });
22
- return super.call(input, context ?? this.context, options);
21
+ return super.invoke(input, context ?? this.context, options);
23
22
  });
24
- async *process(input, context) {
23
+ async process(input, context) {
25
24
  if (this._process) {
26
- yield { delta: { json: await this._process(input, context) } };
27
- return;
25
+ return this._process(input, context);
28
26
  }
29
27
  if (this.activeAgent) {
30
- const [output, agent] = await context.call(this.activeAgent, input, {
28
+ const [output, agent] = await context.invoke(this.activeAgent, input, {
31
29
  returnActiveAgent: true,
32
30
  streaming: true,
33
31
  });
34
32
  agent.then((agent) => {
35
33
  this.activeAgent = agent;
36
34
  });
37
- yield* readableStreamToAsyncIterator(output);
38
- return;
35
+ return output;
39
36
  }
40
37
  const publicTopic = typeof this.publishTopic === "function" ? await this.publishTopic(input) : this.publishTopic;
41
38
  if (publicTopic?.length) {
42
39
  context.publish(publicTopic, createPublishMessage(input, this));
43
- return;
40
+ return {};
44
41
  }
45
42
  throw new Error("UserAgent must have a process function or a publishTopic");
46
43
  }
@@ -69,7 +66,7 @@ export class UserAgent extends Agent {
69
66
  },
70
67
  });
71
68
  }
72
- checkUsageAgentCalls(_context) {
69
+ checkAgentInvokesUsage(_context) {
73
70
  // ignore calls usage check for UserAgent
74
71
  }
75
72
  }
@@ -1,29 +1,26 @@
1
1
  import { Agent } from "../agents/agent.js";
2
2
  import { ChatModel } from "../models/chat-model.js";
3
- import { type Context, ExecutionContext } from "./context.js";
3
+ import { AIGNEContext, type Context } from "./context.js";
4
4
  import { MessageQueue } from "./message-queue.js";
5
5
  import type { ContextLimits } from "./usage.js";
6
- export interface ExecutionEngineOptions {
6
+ export interface AIGNEOptions {
7
7
  name?: string;
8
8
  description?: string;
9
9
  model?: ChatModel;
10
- tools?: Agent[];
10
+ skills?: Agent[];
11
11
  agents?: Agent[];
12
12
  limits?: ContextLimits;
13
13
  }
14
- export interface ExecutionEngineRunOptions {
15
- returnActiveAgent?: boolean;
16
- }
17
- export declare class ExecutionEngine {
14
+ export declare class AIGNE {
18
15
  static load({ path, ...options }: {
19
16
  path: string;
20
- } & ExecutionEngineOptions): Promise<ExecutionEngine>;
21
- constructor(options?: ExecutionEngineOptions);
17
+ } & AIGNEOptions): Promise<AIGNE>;
18
+ constructor(options?: AIGNEOptions);
22
19
  name?: string;
23
20
  description?: string;
24
21
  readonly messageQueue: MessageQueue;
25
22
  model?: ChatModel;
26
- readonly tools: Agent<import("../agents/agent.js").Message, import("../agents/agent.js").Message>[] & {
23
+ readonly skills: Agent<import("../agents/agent.js").Message, import("../agents/agent.js").Message>[] & {
27
24
  [key: string]: Agent<import("../agents/agent.js").Message, import("../agents/agent.js").Message>;
28
25
  };
29
26
  readonly agents: Agent<import("../agents/agent.js").Message, import("../agents/agent.js").Message>[] & {
@@ -31,9 +28,9 @@ export declare class ExecutionEngine {
31
28
  };
32
29
  limits?: ContextLimits;
33
30
  addAgent(...agents: Agent[]): void;
34
- newContext(): ExecutionContext;
31
+ newContext(): AIGNEContext;
35
32
  publish: Context["publish"];
36
- call: Context["call"];
33
+ invoke: Context["invoke"];
37
34
  subscribe: Context["subscribe"];
38
35
  unsubscribe: Context["unsubscribe"];
39
36
  shutdown(): Promise<void>;