@aigne/core 1.33.2 → 1.36.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 (43) hide show
  1. package/CHANGELOG.md +29 -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.js +3 -13
  6. package/lib/cjs/aigne/context.d.ts +1 -0
  7. package/lib/cjs/aigne/context.js +25 -10
  8. package/lib/cjs/loader/agent-js.d.ts +2 -11
  9. package/lib/cjs/loader/agent-js.js +4 -8
  10. package/lib/cjs/loader/agent-yaml.d.ts +18 -2
  11. package/lib/cjs/loader/agent-yaml.js +32 -13
  12. package/lib/cjs/loader/index.d.ts +2 -2
  13. package/lib/cjs/loader/index.js +48 -15
  14. package/lib/cjs/loader/schema.d.ts +10 -0
  15. package/lib/cjs/loader/schema.js +17 -1
  16. package/lib/cjs/package.json +3 -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/aigne/context.d.ts +1 -0
  21. package/lib/dts/loader/agent-js.d.ts +2 -11
  22. package/lib/dts/loader/agent-yaml.d.ts +18 -2
  23. package/lib/dts/loader/index.d.ts +2 -2
  24. package/lib/dts/loader/schema.d.ts +10 -0
  25. package/lib/dts/utils/type-utils.d.ts +1 -1
  26. package/lib/esm/agents/agent.d.ts +71 -10
  27. package/lib/esm/agents/agent.js +73 -28
  28. package/lib/esm/agents/ai-agent.js +6 -3
  29. package/lib/esm/agents/team-agent.js +3 -13
  30. package/lib/esm/aigne/context.d.ts +1 -0
  31. package/lib/esm/aigne/context.js +25 -10
  32. package/lib/esm/loader/agent-js.d.ts +2 -11
  33. package/lib/esm/loader/agent-js.js +5 -6
  34. package/lib/esm/loader/agent-yaml.d.ts +18 -2
  35. package/lib/esm/loader/agent-yaml.js +33 -11
  36. package/lib/esm/loader/index.d.ts +2 -2
  37. package/lib/esm/loader/index.js +49 -16
  38. package/lib/esm/loader/schema.d.ts +10 -0
  39. package/lib/esm/loader/schema.js +12 -0
  40. package/lib/esm/package.json +3 -1
  41. package/lib/esm/utils/type-utils.d.ts +1 -1
  42. package/lib/esm/utils/type-utils.js +2 -4
  43. package/package.json +4 -4
@@ -10,6 +10,7 @@ import { type Nullish, type PromiseOrValue, type XOr } from "../utils/type-utils
10
10
  import type { GuideRailAgent, GuideRailAgentOutput } from "./guide-rail-agent.js";
11
11
  import { type TransferAgentOutput } from "./types.js";
12
12
  export * from "./types.js";
13
+ export declare const DEFAULT_INPUT_ACTION_GET = "$get";
13
14
  /**
14
15
  * Basic message type that can contain any key-value pairs
15
16
  */
@@ -72,6 +73,11 @@ export interface AgentOptions<I extends Message = Message, O extends Message = M
72
73
  * Used to validate that input messages conform to the expected format
73
74
  */
74
75
  inputSchema?: AgentInputOutputSchema<I>;
76
+ /**
77
+ * Default input message for the agent, it can be used to provide partial input
78
+ * or default values for the agent's input schema.
79
+ */
80
+ defaultInput?: Agent<I, O>["defaultInput"];
75
81
  /**
76
82
  * Zod schema defining the output message structure
77
83
  *
@@ -106,7 +112,7 @@ export interface AgentOptions<I extends Message = Message, O extends Message = M
106
112
  * Maximum number of memory items to retrieve
107
113
  */
108
114
  maxRetrieveMemoryCount?: number;
109
- hooks?: AgentHooks<I, O>;
115
+ hooks?: AgentHooks<I, O> | AgentHooks<I, O>[];
110
116
  }
111
117
  export declare const agentOptionsSchema: ZodObject<{
112
118
  [key in keyof AgentOptions]: ZodType<AgentOptions[key]>;
@@ -137,6 +143,10 @@ export interface AgentInvokeOptions<U extends UserContext = UserContext> {
137
143
  * and returns the final JSON result
138
144
  */
139
145
  streaming?: boolean;
146
+ /**
147
+ * Optional hooks for agent invocation
148
+ */
149
+ hooks?: AgentHooks<any, any>;
140
150
  }
141
151
  /**
142
152
  * Agent is the base class for all agents.
@@ -190,7 +200,7 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
190
200
  * Here's an example of using hooks:
191
201
  * {@includeCode ../../test/agents/agent.test.ts#example-agent-hooks}
192
202
  */
193
- readonly hooks: AgentHooks<I, O>;
203
+ readonly hooks: AgentHooks<I, O>[];
194
204
  /**
195
205
  * List of GuideRail agents applied to this agent
196
206
  *
@@ -232,6 +242,11 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
232
242
  */
233
243
  readonly description?: string;
234
244
  private readonly _inputSchema?;
245
+ defaultInput?: Partial<{
246
+ [key in keyof I]: {
247
+ [DEFAULT_INPUT_ACTION_GET]: string;
248
+ } | I[key];
249
+ }>;
235
250
  private readonly _outputSchema?;
236
251
  /**
237
252
  * Get the input data schema for this agent
@@ -372,6 +387,8 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
372
387
  * @returns Agent response (streaming or regular)
373
388
  */
374
389
  invoke(input: I & Message, options?: Partial<AgentInvokeOptions>): Promise<AgentResponse<O>>;
390
+ private callHooks;
391
+ private mergeDefaultInput;
375
392
  protected invokeSkill<I extends Message, O extends Message>(skill: Agent<I, O>, input: I & Message, options: AgentInvokeOptions): Promise<O>;
376
393
  /**
377
394
  * Process agent output
@@ -517,11 +534,15 @@ export interface AgentHooks<I extends Message = Message, O extends Message = Mes
517
534
  *
518
535
  * @param event Object containing the input message
519
536
  */
520
- onStart?: (event: {
537
+ onStart?: ((event: {
521
538
  context: Context;
522
539
  input: I;
523
540
  }) => PromiseOrValue<void | {
524
541
  input?: I;
542
+ }>) | Agent<{
543
+ input: I;
544
+ }, {
545
+ input?: I;
525
546
  }>;
526
547
  /**
527
548
  * Called when agent processing completes or fails
@@ -532,13 +553,39 @@ export interface AgentHooks<I extends Message = Message, O extends Message = Mes
532
553
  *
533
554
  * @param event Object containing the input message and either output or error
534
555
  */
535
- onEnd?: (event: XOr<{
556
+ onEnd?: ((event: XOr<{
536
557
  context: Context;
537
558
  input: I;
538
559
  output: O;
539
560
  error: Error;
540
561
  }, "output", "error">) => PromiseOrValue<void | {
541
562
  output?: O;
563
+ }>) | Agent<XOr<{
564
+ input: I;
565
+ output: O;
566
+ error: Error;
567
+ }, "output", "error">, {
568
+ output?: O;
569
+ }>;
570
+ onSuccess?: ((event: {
571
+ context: Context;
572
+ input: I;
573
+ output: O;
574
+ }) => PromiseOrValue<void | {
575
+ output?: O;
576
+ }>) | Agent<{
577
+ input: I;
578
+ output: O;
579
+ }, {
580
+ output?: O;
581
+ }>;
582
+ onError?: ((event: {
583
+ context: Context;
584
+ input: I;
585
+ error: Error;
586
+ }) => void) | Agent<{
587
+ input: I;
588
+ error: Error;
542
589
  }>;
543
590
  /**
544
591
  * Called before a skill (sub-agent) is invoked
@@ -548,11 +595,14 @@ export interface AgentHooks<I extends Message = Message, O extends Message = Mes
548
595
  *
549
596
  * @param event Object containing the skill being used and input message
550
597
  */
551
- onSkillStart?: (event: {
598
+ onSkillStart?: ((event: {
552
599
  context: Context;
553
600
  skill: Agent;
554
601
  input: Message;
555
- }) => PromiseOrValue<void>;
602
+ }) => PromiseOrValue<void>) | Agent<{
603
+ skill: Agent;
604
+ input: Message;
605
+ }>;
556
606
  /**
557
607
  * Called after a skill (sub-agent) completes or fails
558
608
  *
@@ -562,13 +612,18 @@ export interface AgentHooks<I extends Message = Message, O extends Message = Mes
562
612
  *
563
613
  * @param event Object containing the skill used, input message, and either output or error
564
614
  */
565
- onSkillEnd?: (event: XOr<{
615
+ onSkillEnd?: ((event: XOr<{
566
616
  context: Context;
567
617
  skill: Agent;
568
618
  input: Message;
569
619
  output: Message;
570
620
  error: Error;
571
- }, "output", "error">) => PromiseOrValue<void>;
621
+ }, "output", "error">) => PromiseOrValue<void>) | Agent<XOr<{
622
+ skill: Agent;
623
+ input: Message;
624
+ output: Message;
625
+ error: Error;
626
+ }, "output", "error">>;
572
627
  /**
573
628
  * Called when an agent hands off processing to another agent
574
629
  *
@@ -578,13 +633,19 @@ export interface AgentHooks<I extends Message = Message, O extends Message = Mes
578
633
  *
579
634
  * @param event Object containing the source agent, target agent, and input message
580
635
  */
581
- onHandoff?: (event: {
636
+ onHandoff?: ((event: {
582
637
  context: Context;
583
638
  source: Agent;
584
639
  target: Agent;
585
640
  input: I;
586
- }) => PromiseOrValue<void>;
641
+ }) => PromiseOrValue<void>) | Agent<{
642
+ source: Agent;
643
+ target: Agent;
644
+ input: I;
645
+ }>;
587
646
  }
647
+ export type AgentHookInput<H extends keyof AgentHooks, Hook extends AgentHooks[H] = AgentHooks[H]> = Hook extends (input: infer I) => any ? Omit<I, "context"> : never;
648
+ export type AgentHookOutput<H extends keyof AgentHooks, Hook extends AgentHooks[H] = AgentHooks[H]> = Hook extends (...args: any[]) => any ? Exclude<Awaited<ReturnType<Hook>>, void> | undefined : never;
588
649
  /**
589
650
  * Response type for an agent, can be:
590
651
  * - Direct response object
@@ -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
  }
@@ -165,19 +165,13 @@ export class TeamAgent extends Agent {
165
165
  */
166
166
  async *_processSequential(input, options) {
167
167
  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 });
168
+ for (const agent of this.skills) {
169
+ const o = await options.context.invoke(agent, { ...input, ...output }, { ...options, streaming: true });
173
170
  for await (const chunk of o) {
174
171
  yield chunk;
175
172
  mergeAgentResponseChunk(output, chunk);
176
173
  }
177
- newAgents.push(await transferToAgent);
178
174
  }
179
- this.skills.splice(0);
180
- this.skills.push(...newAgents);
181
175
  }
182
176
  /**
183
177
  * Process input in parallel through all agents in the team.
@@ -194,8 +188,7 @@ export class TeamAgent extends Agent {
194
188
  * @private
195
189
  */
196
190
  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]);
191
+ const streams = await Promise.all(this.skills.map((agent) => options.context.invoke(agent, input, { ...options, streaming: true })));
199
192
  const read = async (index, reader) => {
200
193
  const promise = reader.read();
201
194
  return promise.then((result) => ({ ...result, reader, index }));
@@ -230,8 +223,5 @@ export class TeamAgent extends Agent {
230
223
  yield { delta: { ...delta, text } };
231
224
  }
232
225
  }
233
- const agents = await Promise.all(result.map((i) => i[1]));
234
- this.skills.splice(0);
235
- this.skills.push(...agents);
236
226
  }
237
227
  }
@@ -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,8 @@
1
- import { type ZodObject, type ZodType, z } from "zod";
2
1
  import { Agent, type FunctionAgentFn } from "../agents/agent.js";
3
2
  export declare function loadAgentFromJsFile(path: string): Promise<Agent<any, any> | {
4
3
  process: FunctionAgentFn;
5
4
  name: string;
6
5
  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;
6
+ inputSchema?: any;
7
+ outputSchema?: any;
17
8
  }>;
@@ -1,26 +1,25 @@
1
1
  import { jsonSchemaToZod } from "@aigne/json-schema-to-zod";
2
- import camelize from "camelize-ts";
3
2
  import { z } from "zod";
4
3
  import { Agent } from "../agents/agent.js";
5
4
  import { tryOrThrow } from "../utils/type-utils.js";
6
- import { inputOutputSchema, optionalize } from "./schema.js";
5
+ import { camelizeSchema, inputOutputSchema, optionalize } from "./schema.js";
7
6
  export async function loadAgentFromJsFile(path) {
8
- const agentJsFileSchema = z.object({
7
+ const agentJsFileSchema = camelizeSchema(z.object({
9
8
  name: z.string(),
10
9
  description: optionalize(z.string()),
11
10
  inputSchema: optionalize(inputOutputSchema({ path })).transform((v) => v ? jsonSchemaToZod(v) : undefined),
12
11
  outputSchema: optionalize(inputOutputSchema({ path })).transform((v) => v ? jsonSchemaToZod(v) : undefined),
13
12
  process: z.custom(),
14
- });
13
+ }));
15
14
  const { default: agent } = await tryOrThrow(() => import(/* @vite-ignore */ path), (error) => new Error(`Failed to load agent definition from ${path}: ${error.message}`));
16
15
  if (agent instanceof Agent)
17
16
  return agent;
18
17
  if (typeof agent !== "function") {
19
18
  throw new Error(`Agent file ${path} must export a default function, but got ${typeof agent}`);
20
19
  }
21
- return tryOrThrow(() => agentJsFileSchema.parseAsync(camelize({
20
+ return tryOrThrow(() => agentJsFileSchema.parseAsync({
22
21
  ...agent,
23
22
  name: agent.agent_name || agent.agentName || agent.name,
24
23
  process: agent,
25
- })), (error) => new Error(`Failed to parse agent from ${path}: ${error.message}`));
24
+ }), (error) => new Error(`Failed to parse agent from ${path}: ${error.message}`));
26
25
  }
@@ -1,12 +1,28 @@
1
1
  import { type ZodType } from "zod";
2
2
  import { AIAgentToolChoice } from "../agents/ai-agent.js";
3
3
  import { ProcessMode } from "../agents/team-agent.js";
4
+ export interface HooksSchema {
5
+ onStart?: NestAgentSchema;
6
+ onSuccess?: NestAgentSchema;
7
+ onError?: NestAgentSchema;
8
+ onEnd?: NestAgentSchema;
9
+ onSkillStart?: NestAgentSchema;
10
+ onSkillEnd?: NestAgentSchema;
11
+ onHandoff?: NestAgentSchema;
12
+ }
13
+ export type NestAgentSchema = string | {
14
+ url: string;
15
+ defaultInput?: Record<string, any>;
16
+ hooks?: HooksSchema | HooksSchema[];
17
+ } | AgentSchema;
4
18
  interface BaseAgentSchema {
5
19
  name?: string;
6
20
  description?: string;
7
21
  inputSchema?: ZodType<Record<string, any>>;
22
+ defaultInput?: Record<string, any>;
8
23
  outputSchema?: ZodType<Record<string, any>>;
9
- skills?: (string | AgentSchema)[];
24
+ skills?: NestAgentSchema[];
25
+ hooks?: HooksSchema | HooksSchema[];
10
26
  memory?: boolean | {
11
27
  provider: string;
12
28
  subscribeTopic?: string[];
@@ -19,7 +35,7 @@ interface AIAgentSchema extends BaseAgentSchema {
19
35
  outputKey?: string;
20
36
  toolChoice?: AIAgentToolChoice;
21
37
  }
22
- interface MCPAgentSchema {
38
+ interface MCPAgentSchema extends BaseAgentSchema {
23
39
  type: "mcp";
24
40
  url?: string;
25
41
  command?: string;