@aigne/core 1.34.0 → 1.37.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 (44) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/lib/cjs/agents/agent.d.ts +71 -10
  3. package/lib/cjs/agents/agent.js +73 -28
  4. package/lib/cjs/agents/ai-agent.js +6 -3
  5. package/lib/cjs/agents/team-agent.d.ts +93 -1
  6. package/lib/cjs/agents/team-agent.js +45 -17
  7. package/lib/cjs/aigne/context.d.ts +1 -0
  8. package/lib/cjs/aigne/context.js +25 -10
  9. package/lib/cjs/loader/agent-js.d.ts +2 -17
  10. package/lib/cjs/loader/agent-js.js +4 -16
  11. package/lib/cjs/loader/agent-yaml.d.ts +29 -8
  12. package/lib/cjs/loader/agent-yaml.js +44 -15
  13. package/lib/cjs/loader/index.d.ts +2 -2
  14. package/lib/cjs/loader/index.js +55 -16
  15. package/lib/cjs/loader/schema.d.ts +10 -0
  16. package/lib/cjs/loader/schema.js +17 -1
  17. package/lib/cjs/utils/type-utils.d.ts +1 -1
  18. package/lib/cjs/utils/type-utils.js +2 -4
  19. package/lib/dts/agents/agent.d.ts +71 -10
  20. package/lib/dts/agents/team-agent.d.ts +93 -1
  21. package/lib/dts/aigne/context.d.ts +1 -0
  22. package/lib/dts/loader/agent-js.d.ts +2 -17
  23. package/lib/dts/loader/agent-yaml.d.ts +29 -8
  24. package/lib/dts/loader/index.d.ts +2 -2
  25. package/lib/dts/loader/schema.d.ts +10 -0
  26. package/lib/dts/utils/type-utils.d.ts +1 -1
  27. package/lib/esm/agents/agent.d.ts +71 -10
  28. package/lib/esm/agents/agent.js +73 -28
  29. package/lib/esm/agents/ai-agent.js +6 -3
  30. package/lib/esm/agents/team-agent.d.ts +93 -1
  31. package/lib/esm/agents/team-agent.js +44 -16
  32. package/lib/esm/aigne/context.d.ts +1 -0
  33. package/lib/esm/aigne/context.js +25 -10
  34. package/lib/esm/loader/agent-js.d.ts +2 -17
  35. package/lib/esm/loader/agent-js.js +4 -13
  36. package/lib/esm/loader/agent-yaml.d.ts +29 -8
  37. package/lib/esm/loader/agent-yaml.js +44 -13
  38. package/lib/esm/loader/index.d.ts +2 -2
  39. package/lib/esm/loader/index.js +56 -17
  40. package/lib/esm/loader/schema.d.ts +10 -0
  41. package/lib/esm/loader/schema.js +12 -0
  42. package/lib/esm/utils/type-utils.d.ts +1 -1
  43. package/lib/esm/utils/type-utils.js +2 -4
  44. package/package.json +3 -3
@@ -2,9 +2,17 @@ import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
2
2
  import { ZodObject, z } from "zod";
3
3
  import { logger } from "../utils/logger.js";
4
4
  import { agentResponseStreamToObject, asyncGeneratorToReadableStream, isAsyncGenerator, objectToAgentResponseStream, onAgentResponseStreamEnd, } from "../utils/stream-utils.js";
5
- import { checkArguments, createAccessorArray, flat, isEmpty, isRecord, } from "../utils/type-utils.js";
5
+ import { checkArguments, createAccessorArray, flat, isEmpty, isNil, isRecord, } from "../utils/type-utils.js";
6
6
  import { replaceTransferAgentToName, transferToAgentOutput, } from "./types.js";
7
7
  export * from "./types.js";
8
+ export const DEFAULT_INPUT_ACTION_GET = "$get";
9
+ const hooksSchema = z.object({
10
+ onStart: z.custom().optional(),
11
+ onEnd: z.custom().optional(),
12
+ onSkillStart: z.custom().optional(),
13
+ onSkillEnd: z.custom().optional(),
14
+ onHandoff: z.custom().optional(),
15
+ });
8
16
  export const agentOptionsSchema = z.object({
9
17
  subscribeTopic: z.union([z.string(), z.array(z.string())]).optional(),
10
18
  publishTopic: z
@@ -13,21 +21,14 @@ export const agentOptionsSchema = z.object({
13
21
  name: z.string().optional(),
14
22
  description: z.string().optional(),
15
23
  inputSchema: z.custom().optional(),
24
+ defaultInput: z.record(z.any()).optional(),
16
25
  outputSchema: z.custom().optional(),
17
26
  includeInputInOutput: z.boolean().optional(),
18
27
  skills: z.array(z.union([z.custom(), z.custom()])).optional(),
19
28
  disableEvents: z.boolean().optional(),
20
29
  memory: z.union([z.custom(), z.array(z.custom())]).optional(),
21
30
  maxRetrieveMemoryCount: z.number().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
+ hooks: z.union([z.array(hooksSchema), hooksSchema]).optional(),
31
32
  guideRails: z.array(z.custom()).optional(),
32
33
  });
33
34
  /**
@@ -62,6 +63,7 @@ export class Agent {
62
63
  if (outputSchema)
63
64
  checkAgentInputOutputSchema(outputSchema);
64
65
  this._inputSchema = inputSchema;
66
+ this.defaultInput = options.defaultInput;
65
67
  this._outputSchema = outputSchema;
66
68
  this.includeInputInOutput = options.includeInputInOutput;
67
69
  this.subscribeTopic = options.subscribeTopic;
@@ -76,7 +78,7 @@ export class Agent {
76
78
  this.memories.push(options.memory);
77
79
  }
78
80
  this.maxRetrieveMemoryCount = options.maxRetrieveMemoryCount;
79
- this.hooks = options.hooks ?? {};
81
+ this.hooks = flat(options.hooks);
80
82
  this.guideRails = options.guideRails;
81
83
  }
82
84
  /**
@@ -142,6 +144,7 @@ export class Agent {
142
144
  */
143
145
  description;
144
146
  _inputSchema;
147
+ defaultInput;
145
148
  _outputSchema;
146
149
  /**
147
150
  * Get the input data schema for this agent
@@ -291,17 +294,23 @@ export class Agent {
291
294
  ...options,
292
295
  context: options.context ?? (await this.newDefaultContext()),
293
296
  };
297
+ input = this.mergeDefaultInput(input);
294
298
  logger.debug("Invoke agent %s started with input: %O", this.name, input);
295
299
  if (!this.disableEvents)
296
300
  opts.context.emit("agentStarted", { agent: this, input });
297
301
  try {
298
- let parsedInput = (await this.hooks.onStart?.({ context: opts.context, input }))?.input ?? input;
299
- parsedInput = checkArguments(`Agent ${this.name} input`, this.inputSchema, input);
300
- await this.preprocess(parsedInput, opts);
302
+ let response;
303
+ const s = await this.callHooks("onStart", { input }, opts);
304
+ if (s?.input)
305
+ input = s.input;
306
+ input = checkArguments(`Agent ${this.name} input`, this.inputSchema, input);
307
+ await this.preprocess(input, opts);
301
308
  this.checkContextStatus(opts);
302
- let response = await this.process(parsedInput, opts);
303
- if (response instanceof Agent) {
304
- response = transferToAgentOutput(response);
309
+ if (!response) {
310
+ response = await this.process(input, opts);
311
+ if (response instanceof Agent) {
312
+ response = transferToAgentOutput(response);
313
+ }
305
314
  }
306
315
  if (opts.streaming) {
307
316
  const stream = response instanceof ReadableStream
@@ -311,29 +320,62 @@ export class Agent {
311
320
  : objectToAgentResponseStream(response);
312
321
  return this.checkResponseByGuideRails(input, onAgentResponseStreamEnd(stream, {
313
322
  onResult: async (result) => {
314
- return await this.processAgentOutput(parsedInput, result, opts);
323
+ return await this.processAgentOutput(input, result, opts);
315
324
  },
316
325
  onError: async (error) => {
317
326
  return await this.processAgentError(input, error, opts);
318
327
  },
319
328
  }), opts);
320
329
  }
321
- return await this.checkResponseByGuideRails(input, this.processAgentOutput(parsedInput, await agentProcessResultToObject(response), opts), opts);
330
+ return await this.checkResponseByGuideRails(input, this.processAgentOutput(input, await agentProcessResultToObject(response), opts), opts);
322
331
  }
323
332
  catch (error) {
324
333
  throw await this.processAgentError(input, error, opts);
325
334
  }
326
335
  }
336
+ async callHooks(hook, input, options) {
337
+ const { context } = options;
338
+ const result = {};
339
+ for (const hooks of flat(options.hooks, this.hooks)) {
340
+ const h = hooks[hook];
341
+ if (!h)
342
+ continue;
343
+ if (typeof h === "function") {
344
+ Object.assign(result, await h({ ...input, context }));
345
+ }
346
+ else {
347
+ Object.assign(result, await context.invoke(h, input));
348
+ }
349
+ }
350
+ return result;
351
+ }
352
+ mergeDefaultInput(input) {
353
+ const defaultInput = Object.fromEntries(Object.entries(this.defaultInput ?? {}).filter(([, v]) => !(typeof v === "object" && DEFAULT_INPUT_ACTION_GET in v)));
354
+ input = { ...defaultInput, ...input };
355
+ for (const key of Object.keys(this.defaultInput ?? {})) {
356
+ const v = this.defaultInput?.[key];
357
+ if (v &&
358
+ typeof v === "object" &&
359
+ DEFAULT_INPUT_ACTION_GET in v &&
360
+ typeof v[DEFAULT_INPUT_ACTION_GET] === "string" &&
361
+ isNil(input[key])) {
362
+ const value = input[v[DEFAULT_INPUT_ACTION_GET]];
363
+ if (!isNil(value))
364
+ Object.assign(input, { [key]: value });
365
+ }
366
+ }
367
+ return input;
368
+ }
327
369
  async invokeSkill(skill, input, options) {
328
370
  const { context } = options;
329
- await this.hooks.onSkillStart?.({ context, skill, input });
371
+ await this.callHooks("onSkillStart", { skill, input }, options);
330
372
  try {
331
373
  const output = await context.invoke(skill, input);
332
- await this.hooks.onSkillEnd?.({ context, skill, input, output });
374
+ await this.callHooks("onSkillEnd", { skill, input, output }, options);
333
375
  return output;
334
376
  }
335
377
  catch (error) {
336
- await this.hooks.onSkillEnd?.({ context, skill, input, error });
378
+ await this.callHooks("onSkillEnd", { skill, input, error }, options);
337
379
  throw error;
338
380
  }
339
381
  }
@@ -353,14 +395,17 @@ export class Agent {
353
395
  throw new Error(`expect to return a record type such as {result: ...}, but got (${typeof output}): ${output}`);
354
396
  }
355
397
  const parsedOutput = checkArguments(`Agent ${this.name} output`, this.outputSchema, output);
356
- const finalOutput = this.includeInputInOutput ? { ...input, ...parsedOutput } : parsedOutput;
398
+ let finalOutput = this.includeInputInOutput ? { ...input, ...parsedOutput } : parsedOutput;
357
399
  await this.postprocess(input, finalOutput, options);
358
400
  logger.debug("Invoke agent %s succeed with output: %O", this.name, finalOutput);
359
401
  if (!this.disableEvents)
360
402
  context.emit("agentSucceed", { agent: this, output: finalOutput });
361
- const o = (await this.hooks.onEnd?.({ context, input, output: finalOutput }))?.output;
362
- if (o)
363
- return o;
403
+ let o = await this.callHooks("onSuccess", { input, output: finalOutput }, options);
404
+ if (o?.output)
405
+ finalOutput = o.output;
406
+ o = await this.callHooks("onEnd", { input, output: finalOutput }, options);
407
+ if (o?.output)
408
+ finalOutput = o.output;
364
409
  return finalOutput;
365
410
  }
366
411
  /**
@@ -375,8 +420,8 @@ export class Agent {
375
420
  logger.error("Invoke agent %s failed with error: %O", this.name, error);
376
421
  if (!this.disableEvents)
377
422
  options.context.emit("agentFailed", { agent: this, error });
378
- const { context } = options;
379
- await this.hooks.onEnd?.({ context, input, error });
423
+ await this.callHooks("onError", { input, error }, options);
424
+ await this.callHooks("onEnd", { input, error }, options);
380
425
  return error;
381
426
  }
382
427
  /**
@@ -240,7 +240,7 @@ export class AIAgent extends Agent {
240
240
  const outputKey = this.outputKey;
241
241
  for (;;) {
242
242
  const modelOutput = {};
243
- let stream = await options.context.invoke(model, { ...modelInput, messages: modelInput.messages.concat(toolCallMessages) }, { streaming: true });
243
+ let stream = await options.context.invoke(model, { ...modelInput, messages: modelInput.messages.concat(toolCallMessages) }, { ...options, streaming: true });
244
244
  if (this.structuredStreamMode) {
245
245
  const { metadataStart, metadataEnd, parse } = this.customStructuredStreamInstructions || STRUCTURED_STREAM_INSTRUCTIONS;
246
246
  stream = stream.pipeThrough(new ExtractMetadataTransform({ start: metadataStart, end: metadataEnd, parse }));
@@ -315,14 +315,17 @@ export class AIAgent extends Agent {
315
315
  * @protected
316
316
  */
317
317
  async *_processRouter(input, model, modelInput, options, toolsMap) {
318
- const { toolCalls: [call] = [] } = await options.context.invoke(model, modelInput);
318
+ const { toolCalls: [call] = [] } = await options.context.invoke(model, modelInput, {
319
+ ...options,
320
+ streaming: false,
321
+ });
319
322
  if (!call) {
320
323
  throw new Error("Router toolChoice requires exactly one tool to be executed");
321
324
  }
322
325
  const tool = toolsMap.get(call.function.name);
323
326
  if (!tool)
324
327
  throw new Error(`Tool not found: ${call.function.name}`);
325
- const stream = await options.context.invoke(tool, { ...call.function.arguments, ...input }, { streaming: true, sourceAgent: this });
328
+ const stream = await options.context.invoke(tool, { ...call.function.arguments, ...input }, { ...options, streaming: true, sourceAgent: this });
326
329
  return yield* stream;
327
330
  }
328
331
  }
@@ -22,6 +22,73 @@ export declare enum ProcessMode {
22
22
  */
23
23
  parallel = "parallel"
24
24
  }
25
+ export declare const DEFAULT_REFLECTION_MAX_ITERATIONS = 3;
26
+ /**
27
+ * Configuration for reflection mode processing in TeamAgent.
28
+ *
29
+ * Reflection mode enables iterative refinement of agent outputs through a review process.
30
+ * The team agent will repeatedly process the input and have a reviewer agent evaluate
31
+ * the output until it meets approval criteria or reaches the maximum iteration limit.
32
+ *
33
+ * This is particularly useful for:
34
+ * - Quality assurance workflows where outputs need validation
35
+ * - Iterative improvement processes where initial results can be refined
36
+ * - Self-correcting agent systems that learn from feedback
37
+ */
38
+ export interface ReflectionMode {
39
+ /**
40
+ * The agent responsible for reviewing and providing feedback on the team's output.
41
+ *
42
+ * The reviewer agent receives the combined output from the team's processing
43
+ * and should provide feedback or evaluation that can be used to determine
44
+ * whether the output meets the required standards.
45
+ */
46
+ reviewer: Agent;
47
+ /**
48
+ * Function that determines whether the reviewer's output indicates approval.
49
+ *
50
+ * This function receives the reviewer agent's output message and should return:
51
+ * - `true` or truthy value: The output is approved and processing should stop
52
+ * - `false` or falsy value: The output needs improvement and another iteration should run
53
+ *
54
+ * The function can be synchronous or asynchronous, allowing for complex approval logic
55
+ * including external validation, scoring systems, or human-in-the-loop approval.
56
+ *
57
+ * @param output - The message output from the reviewer agent
58
+ * @returns A boolean or truthy/falsy value indicating approval status
59
+ */
60
+ isApproved: (output: Message) => PromiseOrValue<boolean | unknown>;
61
+ /**
62
+ * Maximum number of reflection iterations before giving up.
63
+ *
64
+ * This prevents infinite loops when the approval criteria cannot be met.
65
+ * If the maximum iterations are reached without approval, the process will
66
+ * throw an error indicating the reflection failed to converge.
67
+ *
68
+ * @default 3
69
+ */
70
+ maxIterations?: number;
71
+ /**
72
+ * Controls the behavior when maximum iterations are reached without approval.
73
+ *
74
+ * When set to `true`, the TeamAgent will return the last generated output
75
+ * instead of throwing an error when the maximum number of reflection iterations
76
+ * is reached without the reviewer's approval.
77
+ *
78
+ * When set to `false` or undefined, the TeamAgent will throw an error
79
+ * indicating that the reflection process failed to converge within the
80
+ * maximum iteration limit.
81
+ *
82
+ * This option is useful for scenarios where:
83
+ * - You want to get the best available result even if it's not perfect
84
+ * - The approval criteria might be too strict for the given context
85
+ * - You prefer graceful degradation over complete failure
86
+ * - You want to implement custom error handling based on the returned result
87
+ *
88
+ * @default false
89
+ */
90
+ returnLastOnMaxIterations?: boolean;
91
+ }
25
92
  /**
26
93
  * Configuration options for creating a TeamAgent.
27
94
  *
@@ -33,6 +100,21 @@ export interface TeamAgentOptions<I extends Message, O extends Message> extends
33
100
  * @default {ProcessMode.sequential}
34
101
  */
35
102
  mode?: ProcessMode;
103
+ /**
104
+ * Configuration for reflection mode processing.
105
+ *
106
+ * When enabled, the TeamAgent will use an iterative refinement process where:
107
+ * 1. The team processes the input normally
108
+ * 2. A reviewer agent evaluates the output
109
+ * 3. If not approved, the process repeats with the previous output as context
110
+ * 4. This continues until approval or maximum iterations are reached
111
+ *
112
+ * This enables self-improving agent workflows that can iteratively refine
113
+ * their outputs based on feedback from a specialized reviewer agent.
114
+ *
115
+ * @see ReflectionMode for detailed configuration options
116
+ */
117
+ reflection?: ReflectionMode;
36
118
  /**
37
119
  * Specifies which input field should be treated as an array for iterative processing.
38
120
  *
@@ -116,6 +198,14 @@ export declare class TeamAgent<I extends Message, O extends Message> extends Age
116
198
  * This can be either sequential (one after another) or parallel (all at once).
117
199
  */
118
200
  mode: ProcessMode;
201
+ /**
202
+ * The reflection mode configuration with guaranteed maxIterations value.
203
+ *
204
+ * This is the internal representation after processing the user-provided
205
+ * reflection configuration, ensuring that maxIterations always has a value
206
+ * (defaulting to DEFAULT_REFLECTION_MAX_ITERATIONS if not specified).
207
+ */
208
+ reflection?: ReflectionMode & Required<Pick<ReflectionMode, "maxIterations">>;
119
209
  /**
120
210
  * The input field key to iterate over when processing array inputs.
121
211
  *
@@ -148,8 +238,10 @@ export declare class TeamAgent<I extends Message, O extends Message> extends Age
148
238
  * @returns A stream of message chunks that collectively form the response
149
239
  */
150
240
  process(input: I, options: AgentInvokeOptions): PromiseOrValue<AgentProcessResult<O>>;
241
+ private _processNonReflection;
242
+ private _processReflection;
151
243
  private _processIterator;
152
- private _process;
244
+ private _processNonIterator;
153
245
  /**
154
246
  * Process input sequentially through each agent in the team.
155
247
  *
@@ -26,6 +26,7 @@ export var ProcessMode;
26
26
  */
27
27
  ProcessMode["parallel"] = "parallel";
28
28
  })(ProcessMode || (ProcessMode = {}));
29
+ export const DEFAULT_REFLECTION_MAX_ITERATIONS = 3;
29
30
  /**
30
31
  * TeamAgent coordinates a group of agents working together to accomplish tasks.
31
32
  *
@@ -70,6 +71,10 @@ export class TeamAgent extends Agent {
70
71
  constructor(options) {
71
72
  super(options);
72
73
  this.mode = options.mode ?? ProcessMode.sequential;
74
+ this.reflection = options.reflection && {
75
+ ...options.reflection,
76
+ maxIterations: options.reflection.maxIterations ?? DEFAULT_REFLECTION_MAX_ITERATIONS,
77
+ };
73
78
  this.iterateOn = options.iterateOn;
74
79
  this.iterateWithPreviousOutput = options.iterateWithPreviousOutput;
75
80
  }
@@ -79,6 +84,14 @@ export class TeamAgent extends Agent {
79
84
  * This can be either sequential (one after another) or parallel (all at once).
80
85
  */
81
86
  mode;
87
+ /**
88
+ * The reflection mode configuration with guaranteed maxIterations value.
89
+ *
90
+ * This is the internal representation after processing the user-provided
91
+ * reflection configuration, ensuring that maxIterations always has a value
92
+ * (defaulting to DEFAULT_REFLECTION_MAX_ITERATIONS if not specified).
93
+ */
94
+ reflection;
82
95
  /**
83
96
  * The input field key to iterate over when processing array inputs.
84
97
  *
@@ -113,10 +126,35 @@ export class TeamAgent extends Agent {
113
126
  process(input, options) {
114
127
  if (!this.skills.length)
115
128
  throw new Error("TeamAgent must have at least one skill defined.");
129
+ if (this.reflection)
130
+ return this._processReflection(input, options);
131
+ return this._processNonReflection(input, options);
132
+ }
133
+ _processNonReflection(input, options) {
116
134
  if (this.iterateOn) {
117
135
  return this._processIterator(this.iterateOn, input, options);
118
136
  }
119
- return this._process(input, options);
137
+ return this._processNonIterator(input, options);
138
+ }
139
+ async _processReflection(input, options) {
140
+ assert(this.reflection, "Reflection mode must be defined for reflection processing");
141
+ let iterations = 0;
142
+ const previousOutput = { ...input };
143
+ for (;;) {
144
+ const output = await agentProcessResultToObject(await this._processNonReflection(previousOutput, options));
145
+ Object.assign(previousOutput, output);
146
+ const reviewOutput = await options.context.invoke(this.reflection.reviewer, previousOutput);
147
+ Object.assign(previousOutput, reviewOutput);
148
+ const approved = await this.reflection.isApproved(reviewOutput);
149
+ if (approved)
150
+ return output;
151
+ if (++iterations >= this.reflection.maxIterations) {
152
+ if (this.reflection.returnLastOnMaxIterations)
153
+ return output;
154
+ break;
155
+ }
156
+ }
157
+ throw new Error(`Reflection mode exceeded max iterations ${this.reflection.maxIterations}. Please review the feedback and try again.`);
120
158
  }
121
159
  async *_processIterator(key, input, options) {
122
160
  assert(this.iterateOn, "iterateInputKey must be defined for iterator processing");
@@ -127,7 +165,7 @@ export class TeamAgent extends Agent {
127
165
  const item = arr[i];
128
166
  if (!isRecord(item))
129
167
  throw new TypeError(`Expected ${String(key)} to be an object, got ${typeof item}`);
130
- const res = await agentProcessResultToObject(await this._process({ ...input, [key]: arr, ...item }, { ...options, streaming: false }));
168
+ const res = await agentProcessResultToObject(await this._processNonIterator({ ...input, [key]: arr, ...item }, { ...options, streaming: false }));
131
169
  // Merge the item result with the original item used for next iteration
132
170
  if (this.iterateWithPreviousOutput) {
133
171
  arr = produce(arr, (draft) => {
@@ -140,7 +178,7 @@ export class TeamAgent extends Agent {
140
178
  yield { delta: { json: { [key]: result } } };
141
179
  }
142
180
  }
143
- _process(input, options) {
181
+ _processNonIterator(input, options) {
144
182
  switch (this.mode) {
145
183
  case ProcessMode.sequential:
146
184
  return this._processSequential(input, options);
@@ -165,19 +203,13 @@ export class TeamAgent extends Agent {
165
203
  */
166
204
  async *_processSequential(input, options) {
167
205
  const output = {};
168
- // Clone the agents to run, so that we can update the agents list during the loop
169
- const agents = [...this.skills];
170
- const newAgents = [];
171
- for (const agent of agents) {
172
- const [o, transferToAgent] = await options.context.invoke(agent, { ...input, ...output }, { returnActiveAgent: true, streaming: true });
206
+ for (const agent of this.skills) {
207
+ const o = await options.context.invoke(agent, { ...input, ...output }, { ...options, streaming: true });
173
208
  for await (const chunk of o) {
174
209
  yield chunk;
175
210
  mergeAgentResponseChunk(output, chunk);
176
211
  }
177
- newAgents.push(await transferToAgent);
178
212
  }
179
- this.skills.splice(0);
180
- this.skills.push(...newAgents);
181
213
  }
182
214
  /**
183
215
  * Process input in parallel through all agents in the team.
@@ -194,8 +226,7 @@ export class TeamAgent extends Agent {
194
226
  * @private
195
227
  */
196
228
  async *_processParallel(input, options) {
197
- const result = await Promise.all(this.skills.map((agent) => options.context.invoke(agent, input, { returnActiveAgent: true, streaming: true })));
198
- const streams = result.map((i) => i[0]);
229
+ const streams = await Promise.all(this.skills.map((agent) => options.context.invoke(agent, input, { ...options, streaming: true })));
199
230
  const read = async (index, reader) => {
200
231
  const promise = reader.read();
201
232
  return promise.then((result) => ({ ...result, reader, index }));
@@ -230,8 +261,5 @@ export class TeamAgent extends Agent {
230
261
  yield { delta: { ...delta, text } };
231
262
  }
232
263
  }
233
- const agents = await Promise.all(result.map((i) => i[1]));
234
- this.skills.splice(0);
235
- this.skills.push(...agents);
236
264
  }
237
265
  }
@@ -180,6 +180,7 @@ declare class AIGNEContextShared {
180
180
  span?: Span;
181
181
  constructor(parent?: (Pick<Context, "model" | "skills" | "limits" | "observer"> & {
182
182
  messageQueue?: MessageQueue;
183
+ events?: Emitter<any>;
183
184
  }) | undefined);
184
185
  readonly messageQueue: MessageQueue;
185
186
  readonly events: Emitter<any>;
@@ -34,7 +34,7 @@ export class AIGNEContext {
34
34
  }
35
35
  }
36
36
  else {
37
- this.internal = new AIGNEContextShared(parent);
37
+ this.internal = new AIGNEContextShared(parent instanceof AIGNEContext ? parent.internal : parent);
38
38
  this.span = tracer?.startSpan("AIGNEContext");
39
39
  // 修改了 rootId 是否会之前的有影响?,之前为 this.id
40
40
  this.rootId = this.span?.spanContext?.().traceId ?? v7();
@@ -265,9 +265,10 @@ class AIGNEContextShared {
265
265
  constructor(parent) {
266
266
  this.parent = parent;
267
267
  this.messageQueue = this.parent?.messageQueue ?? new MessageQueue();
268
+ this.events = this.parent?.events ?? new Emitter();
268
269
  }
269
270
  messageQueue;
270
- events = new Emitter();
271
+ events;
271
272
  get model() {
272
273
  return this.parent?.model;
273
274
  }
@@ -302,21 +303,35 @@ class AIGNEContextShared {
302
303
  this.initTimeout();
303
304
  return withAbortSignal(this.abortController.signal, new Error("AIGNEContext is timeout"), () => this.invokeAgent(agent, input, context, options));
304
305
  }
305
- async *invokeAgent(agent, input, context, options) {
306
+ async *invokeAgent(agent, input, context, options = {}) {
306
307
  const startedAt = Date.now();
307
308
  try {
308
309
  let activeAgent = agent;
309
310
  for (;;) {
310
311
  const result = {};
311
312
  if (options?.sourceAgent && activeAgent !== options.sourceAgent) {
312
- options.sourceAgent.hooks.onHandoff?.({
313
- context,
314
- source: options.sourceAgent,
315
- target: activeAgent,
316
- input,
317
- });
313
+ for (const { onHandoff } of [options.hooks ?? {}, ...options.sourceAgent.hooks]) {
314
+ if (!onHandoff)
315
+ continue;
316
+ await (typeof onHandoff === "function"
317
+ ? onHandoff({
318
+ context,
319
+ source: options.sourceAgent,
320
+ target: activeAgent,
321
+ input,
322
+ })
323
+ : context.invoke(onHandoff, {
324
+ source: options.sourceAgent,
325
+ target: activeAgent,
326
+ input,
327
+ }));
328
+ }
318
329
  }
319
- const stream = await activeAgent.invoke(input, { ...options, context, streaming: true });
330
+ const stream = await activeAgent.invoke(input, {
331
+ hooks: options.hooks,
332
+ context,
333
+ streaming: true,
334
+ });
320
335
  for await (const value of stream) {
321
336
  if (isAgentResponseDelta(value)) {
322
337
  if (value.delta.json) {
@@ -1,17 +1,2 @@
1
- import { type ZodObject, type ZodType, z } from "zod";
2
- import { Agent, type FunctionAgentFn } from "../agents/agent.js";
3
- export declare function loadAgentFromJsFile(path: string): Promise<Agent<any, any> | {
4
- process: FunctionAgentFn;
5
- name: string;
6
- description?: string | undefined;
7
- inputSchema?: ZodObject<Record<string, ZodType<any, z.ZodTypeDef, any>>, z.UnknownKeysParam, z.ZodTypeAny, {
8
- [x: string]: any;
9
- }, {
10
- [x: string]: any;
11
- }> | undefined;
12
- outputSchema?: ZodObject<Record<string, ZodType<any, z.ZodTypeDef, any>>, z.UnknownKeysParam, z.ZodTypeAny, {
13
- [x: string]: any;
14
- }, {
15
- [x: string]: any;
16
- }> | undefined;
17
- }>;
1
+ import { Agent } from "../agents/agent.js";
2
+ export declare function loadAgentFromJsFile(path: string): Promise<Agent<any, any> | import("./agent-yaml.js").AgentSchema>;
@@ -1,26 +1,17 @@
1
- import { jsonSchemaToZod } from "@aigne/json-schema-to-zod";
2
- import camelize from "camelize-ts";
3
- import { z } from "zod";
4
1
  import { Agent } from "../agents/agent.js";
5
2
  import { tryOrThrow } from "../utils/type-utils.js";
6
- import { inputOutputSchema, optionalize } from "./schema.js";
3
+ import { parseAgentFile } from "./agent-yaml.js";
7
4
  export async function loadAgentFromJsFile(path) {
8
- const agentJsFileSchema = z.object({
9
- name: z.string(),
10
- description: optionalize(z.string()),
11
- inputSchema: optionalize(inputOutputSchema({ path })).transform((v) => v ? jsonSchemaToZod(v) : undefined),
12
- outputSchema: optionalize(inputOutputSchema({ path })).transform((v) => v ? jsonSchemaToZod(v) : undefined),
13
- process: z.custom(),
14
- });
15
5
  const { default: agent } = await tryOrThrow(() => import(/* @vite-ignore */ path), (error) => new Error(`Failed to load agent definition from ${path}: ${error.message}`));
16
6
  if (agent instanceof Agent)
17
7
  return agent;
18
8
  if (typeof agent !== "function") {
19
9
  throw new Error(`Agent file ${path} must export a default function, but got ${typeof agent}`);
20
10
  }
21
- return tryOrThrow(() => agentJsFileSchema.parseAsync(camelize({
11
+ return tryOrThrow(() => parseAgentFile(path, {
22
12
  ...agent,
13
+ type: "function",
23
14
  name: agent.agent_name || agent.agentName || agent.name,
24
15
  process: agent,
25
- })), (error) => new Error(`Failed to parse agent from ${path}: ${error.message}`));
16
+ }), (error) => new Error(`Failed to parse agent from ${path}: ${error.message}`));
26
17
  }