@aigne/core 1.47.0 → 1.49.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.
package/CHANGELOG.md CHANGED
@@ -1,16 +1,30 @@
1
- ## [1.22.0](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.21.0...core-v1.22.0) (2025-06-24)
1
+ # Changelog
2
+
3
+ ## [1.49.0](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.48.0...core-v1.49.0) (2025-08-12)
2
4
 
3
5
 
4
6
  ### Features
5
7
 
6
- * support observability for cli and blocklet ([#155](https://github.com/AIGNE-io/aigne-framework/issues/155)) ([5baa705](https://github.com/AIGNE-io/aigne-framework/commit/5baa705a33cfdba1efc5ccbe18674c27513ca97d))
8
+ * **cli:** add retry functionality and improve error handling for AIGNE Hub ([#348](https://github.com/AIGNE-io/aigne-framework/issues/348)) ([672c93a](https://github.com/AIGNE-io/aigne-framework/commit/672c93abbba8b4b234f6d810536ff4b603a97e1e))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **core:** examples cases that failed when using aigne-hub ([#337](https://github.com/AIGNE-io/aigne-framework/issues/337)) ([0d4a31c](https://github.com/AIGNE-io/aigne-framework/commit/0d4a31c24d9e7d26f00d1accb80719d9ad79a4c6))
7
14
 
8
15
 
9
16
  ### Dependencies
10
17
 
11
18
  * The following workspace dependencies were updated
12
19
  * dependencies
13
- * @aigne/observability bumped to 0.1.0
20
+ * @aigne/platform-helpers bumped to 0.6.1
21
+
22
+ ## [1.48.0](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.47.0...core-v1.48.0) (2025-08-12)
23
+
24
+
25
+ ### Features
26
+
27
+ * enhance task title functionality to support dynamic generation ([#346](https://github.com/AIGNE-io/aigne-framework/issues/346)) ([fff098c](https://github.com/AIGNE-io/aigne-framework/commit/fff098c9828beca9d99e4b2ebaebdf6b92efb84e))
14
28
 
15
29
  ## [1.47.0](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.46.1...core-v1.47.0) (2025-08-11)
16
30
 
@@ -445,6 +459,21 @@
445
459
  * dependencies
446
460
  * @aigne/observability bumped to 0.1.1
447
461
 
462
+ ## [1.22.0](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.21.0...core-v1.22.0) (2025-06-24)
463
+
464
+
465
+ ### Features
466
+
467
+ * support observability for cli and blocklet ([#155](https://github.com/AIGNE-io/aigne-framework/issues/155)) ([5baa705](https://github.com/AIGNE-io/aigne-framework/commit/5baa705a33cfdba1efc5ccbe18674c27513ca97d))
468
+
469
+
470
+ ### Dependencies
471
+
472
+ * The following workspace dependencies were updated
473
+ * dependencies
474
+ * @aigne/observability bumped to 0.1.0
475
+
476
+
448
477
  ## [1.21.0](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.20.1...core-v1.21.0) (2025-06-20)
449
478
 
450
479
 
@@ -72,7 +72,7 @@ export interface AgentOptions<I extends Message = Message, O extends Message = M
72
72
  * for documentation and debugging
73
73
  */
74
74
  description?: string;
75
- taskTitle?: string;
75
+ taskTitle?: string | ((input: I) => PromiseOrValue<string | undefined>);
76
76
  /**
77
77
  * Zod schema defining the input message structure
78
78
  *
@@ -257,8 +257,8 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
257
257
  * each other's roles in a multi-agent system
258
258
  */
259
259
  readonly description?: string;
260
- taskTitle?: string;
261
- renderTaskTitle(input: I): string | undefined;
260
+ taskTitle?: string | ((input: Message) => PromiseOrValue<string | undefined>);
261
+ renderTaskTitle(input: I): Promise<string | undefined>;
262
262
  private readonly _inputSchema?;
263
263
  defaultInput?: Partial<{
264
264
  [key in keyof I]: {
@@ -405,6 +405,7 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
405
405
  * @returns Agent response (streaming or regular)
406
406
  */
407
407
  invoke(input: I & Message, options?: Partial<AgentInvokeOptions>): Promise<AgentResponse<O>>;
408
+ private processStreamingAndRetry;
408
409
  private callHooks;
409
410
  private mergeDefaultInput;
410
411
  protected invokeSkill<I extends Message, O extends Message>(skill: Agent<I, O>, input: I & Message, options: AgentInvokeOptions): Promise<O>;
@@ -582,12 +583,14 @@ export interface AgentHooks<I extends Message = Message, O extends Message = Mes
582
583
  error: Error;
583
584
  }, "output", "error">) => PromiseOrValue<void | {
584
585
  output?: O;
586
+ retry?: boolean;
585
587
  }>) | Agent<XOr<{
586
588
  input: I;
587
589
  output: O;
588
590
  error: Error;
589
591
  }, "output", "error">, {
590
592
  output?: O;
593
+ retry?: boolean;
591
594
  }>;
592
595
  onSuccess?: ((event: {
593
596
  context: Context;
@@ -607,9 +610,13 @@ export interface AgentHooks<I extends Message = Message, O extends Message = Mes
607
610
  agent: Agent;
608
611
  input: I;
609
612
  error: Error;
610
- }) => void) | Agent<{
613
+ }) => PromiseOrValue<void | {
614
+ retry?: boolean;
615
+ }>) | Agent<{
611
616
  input: I;
612
617
  error: Error;
618
+ }, {
619
+ retry?: boolean;
613
620
  }>;
614
621
  /**
615
622
  * Called before a skill (sub-agent) is invoked
@@ -47,6 +47,7 @@ exports.textDelta = textDelta;
47
47
  exports.jsonDelta = jsonDelta;
48
48
  exports.agentProcessResultToObject = agentProcessResultToObject;
49
49
  const index_js_1 = require("@aigne/platform-helpers/nodejs/index.js");
50
+ const fast_deep_equal_1 = __importDefault(require("fast-deep-equal"));
50
51
  const nunjucks_1 = __importDefault(require("nunjucks"));
51
52
  const zod_1 = require("zod");
52
53
  const logger_js_1 = require("../utils/logger.js");
@@ -199,10 +200,13 @@ class Agent {
199
200
  */
200
201
  description;
201
202
  taskTitle;
202
- renderTaskTitle(input) {
203
+ async renderTaskTitle(input) {
203
204
  if (!this.taskTitle)
204
205
  return;
205
- return nunjucks_1.default.renderString(this.taskTitle, { ...input });
206
+ const s = typeof this.taskTitle === "function" ? await this.taskTitle(input) : this.taskTitle;
207
+ if (!s)
208
+ return;
209
+ return nunjucks_1.default.renderString(s, { ...input });
206
210
  }
207
211
  _inputSchema;
208
212
  defaultInput;
@@ -360,7 +364,6 @@ class Agent {
360
364
  if (!this.disableEvents)
361
365
  opts.context.emit("agentStarted", { agent: this, input });
362
366
  try {
363
- let response;
364
367
  const s = await this.callHooks("onStart", { input }, opts);
365
368
  if (s?.input)
366
369
  input = s.input;
@@ -369,31 +372,47 @@ class Agent {
369
372
  input = (0, type_utils_js_1.checkArguments)(`Agent ${this.name} input`, this.inputSchema, input);
370
373
  await this.preprocess(input, opts);
371
374
  this.checkContextStatus(opts);
372
- if (!response) {
373
- response = await this.process(input, opts);
374
- if (response instanceof Agent) {
375
- response = (0, types_js_1.transferToAgentOutput)(response);
375
+ const response = (0, stream_utils_js_1.asyncGeneratorToReadableStream)(this.processStreamingAndRetry(input, opts));
376
+ return await this.checkResponseByGuideRails(input, options.streaming ? response : await agentProcessResultToObject(response), opts);
377
+ }
378
+ catch (error) {
379
+ throw (await this.processAgentError(input, error, opts)).error ?? error;
380
+ }
381
+ }
382
+ async *processStreamingAndRetry(input, options) {
383
+ let output = {};
384
+ for (;;) {
385
+ // Reset output to avoid accumulating old data
386
+ const resetOutput = Object.fromEntries(Object.entries(output).map(([key]) => [key, null]));
387
+ if (!(0, type_utils_js_1.isEmpty)(resetOutput)) {
388
+ yield { delta: { json: resetOutput } };
389
+ output = {};
390
+ }
391
+ let response = await this.process(input, options);
392
+ if (response instanceof Agent)
393
+ response = (0, types_js_1.transferToAgentOutput)(response);
394
+ const stream = response instanceof ReadableStream
395
+ ? response
396
+ : (0, stream_utils_js_1.isAsyncGenerator)(response)
397
+ ? (0, stream_utils_js_1.asyncGeneratorToReadableStream)(response)
398
+ : (0, stream_utils_js_1.objectToAgentResponseStream)(response);
399
+ try {
400
+ for await (const chunk of stream) {
401
+ (0, stream_utils_js_1.mergeAgentResponseChunk)(output, chunk);
402
+ yield chunk;
403
+ }
404
+ const result = await this.processAgentOutput(input, output, options);
405
+ if (result && !(0, fast_deep_equal_1.default)(result, output)) {
406
+ yield { delta: { json: result } };
376
407
  }
408
+ // Close the stream after processing
409
+ break;
377
410
  }
378
- if (opts.streaming) {
379
- const stream = response instanceof ReadableStream
380
- ? response
381
- : (0, stream_utils_js_1.isAsyncGenerator)(response)
382
- ? (0, stream_utils_js_1.asyncGeneratorToReadableStream)(response)
383
- : (0, stream_utils_js_1.objectToAgentResponseStream)(response);
384
- return this.checkResponseByGuideRails(input, (0, stream_utils_js_1.onAgentResponseStreamEnd)(stream, {
385
- onResult: async (result) => {
386
- return await this.processAgentOutput(input, result, opts);
387
- },
388
- onError: async (error) => {
389
- return await this.processAgentError(input, error, opts);
390
- },
391
- }), opts);
411
+ catch (error) {
412
+ const res = await this.processAgentError(input, error, options);
413
+ if (!res.retry)
414
+ throw res.error ?? error;
392
415
  }
393
- return await this.checkResponseByGuideRails(input, this.processAgentOutput(input, await agentProcessResultToObject(response), opts), opts);
394
- }
395
- catch (error) {
396
- throw await this.processAgentError(input, error, opts);
397
416
  }
398
417
  }
399
418
  async callHooks(hook, input, options) {
@@ -480,12 +499,15 @@ class Agent {
480
499
  * @param options Invocation options
481
500
  */
482
501
  async processAgentError(input, error, options) {
502
+ if ("$error_has_been_processed" in error && error.$error_has_been_processed)
503
+ return {};
504
+ Object.defineProperty(error, "$error_has_been_processed", { value: true, enumerable: false });
483
505
  logger_js_1.logger.error("Invoke agent %s failed with error: %O", this.name, error);
484
506
  if (!this.disableEvents)
485
507
  options.context.emit("agentFailed", { agent: this, error });
486
- await this.callHooks("onError", { input, error }, options);
487
- await this.callHooks("onEnd", { input, error }, options);
488
- return error;
508
+ const res = (await this.callHooks("onError", { input, error }, options)) ?? {};
509
+ Object.assign(res, await this.callHooks("onEnd", { input, error }, options));
510
+ return { ...res };
489
511
  }
490
512
  /**
491
513
  * Check agent invocation usage to prevent exceeding limits
@@ -11,6 +11,7 @@ export interface LoadOptions {
11
11
  new (parameters?: MemoryAgentOptions): MemoryAgent;
12
12
  }[];
13
13
  path: string;
14
+ model?: ChatModel;
14
15
  }
15
16
  export declare function load(options: LoadOptions): Promise<AIGNEOptions>;
16
17
  export declare function loadAgent(path: string, options?: LoadOptions, agentOptions?: AgentOptions): Promise<Agent>;
@@ -25,7 +25,7 @@ async function load(options) {
25
25
  return {
26
26
  ...aigne,
27
27
  rootDir,
28
- model: await options.loadModel(aigne.model),
28
+ model: options?.model || (await options.loadModel(aigne.model)),
29
29
  agents: pickAgents(aigne.agents ?? []),
30
30
  skills: pickAgents(aigne.skills ?? []),
31
31
  mcpServer: {
@@ -131,8 +131,8 @@ class AgentResponseProgressStream extends ReadableStream {
131
131
  context.off("agentFailed", onAgentFailed);
132
132
  controller.close();
133
133
  };
134
- const onAgentStarted = (event) => {
135
- const taskTitle = event.agent.renderTaskTitle(event.input);
134
+ const onAgentStarted = async (event) => {
135
+ const taskTitle = await event.agent.renderTaskTitle(event.input);
136
136
  writeEvent("agentStarted", { ...event, taskTitle });
137
137
  };
138
138
  const onAgentSucceed = (event) => {
@@ -72,7 +72,7 @@ export interface AgentOptions<I extends Message = Message, O extends Message = M
72
72
  * for documentation and debugging
73
73
  */
74
74
  description?: string;
75
- taskTitle?: string;
75
+ taskTitle?: string | ((input: I) => PromiseOrValue<string | undefined>);
76
76
  /**
77
77
  * Zod schema defining the input message structure
78
78
  *
@@ -257,8 +257,8 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
257
257
  * each other's roles in a multi-agent system
258
258
  */
259
259
  readonly description?: string;
260
- taskTitle?: string;
261
- renderTaskTitle(input: I): string | undefined;
260
+ taskTitle?: string | ((input: Message) => PromiseOrValue<string | undefined>);
261
+ renderTaskTitle(input: I): Promise<string | undefined>;
262
262
  private readonly _inputSchema?;
263
263
  defaultInput?: Partial<{
264
264
  [key in keyof I]: {
@@ -405,6 +405,7 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
405
405
  * @returns Agent response (streaming or regular)
406
406
  */
407
407
  invoke(input: I & Message, options?: Partial<AgentInvokeOptions>): Promise<AgentResponse<O>>;
408
+ private processStreamingAndRetry;
408
409
  private callHooks;
409
410
  private mergeDefaultInput;
410
411
  protected invokeSkill<I extends Message, O extends Message>(skill: Agent<I, O>, input: I & Message, options: AgentInvokeOptions): Promise<O>;
@@ -582,12 +583,14 @@ export interface AgentHooks<I extends Message = Message, O extends Message = Mes
582
583
  error: Error;
583
584
  }, "output", "error">) => PromiseOrValue<void | {
584
585
  output?: O;
586
+ retry?: boolean;
585
587
  }>) | Agent<XOr<{
586
588
  input: I;
587
589
  output: O;
588
590
  error: Error;
589
591
  }, "output", "error">, {
590
592
  output?: O;
593
+ retry?: boolean;
591
594
  }>;
592
595
  onSuccess?: ((event: {
593
596
  context: Context;
@@ -607,9 +610,13 @@ export interface AgentHooks<I extends Message = Message, O extends Message = Mes
607
610
  agent: Agent;
608
611
  input: I;
609
612
  error: Error;
610
- }) => void) | Agent<{
613
+ }) => PromiseOrValue<void | {
614
+ retry?: boolean;
615
+ }>) | Agent<{
611
616
  input: I;
612
617
  error: Error;
618
+ }, {
619
+ retry?: boolean;
613
620
  }>;
614
621
  /**
615
622
  * Called before a skill (sub-agent) is invoked
@@ -11,6 +11,7 @@ export interface LoadOptions {
11
11
  new (parameters?: MemoryAgentOptions): MemoryAgent;
12
12
  }[];
13
13
  path: string;
14
+ model?: ChatModel;
14
15
  }
15
16
  export declare function load(options: LoadOptions): Promise<AIGNEOptions>;
16
17
  export declare function loadAgent(path: string, options?: LoadOptions, agentOptions?: AgentOptions): Promise<Agent>;
@@ -72,7 +72,7 @@ export interface AgentOptions<I extends Message = Message, O extends Message = M
72
72
  * for documentation and debugging
73
73
  */
74
74
  description?: string;
75
- taskTitle?: string;
75
+ taskTitle?: string | ((input: I) => PromiseOrValue<string | undefined>);
76
76
  /**
77
77
  * Zod schema defining the input message structure
78
78
  *
@@ -257,8 +257,8 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
257
257
  * each other's roles in a multi-agent system
258
258
  */
259
259
  readonly description?: string;
260
- taskTitle?: string;
261
- renderTaskTitle(input: I): string | undefined;
260
+ taskTitle?: string | ((input: Message) => PromiseOrValue<string | undefined>);
261
+ renderTaskTitle(input: I): Promise<string | undefined>;
262
262
  private readonly _inputSchema?;
263
263
  defaultInput?: Partial<{
264
264
  [key in keyof I]: {
@@ -405,6 +405,7 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
405
405
  * @returns Agent response (streaming or regular)
406
406
  */
407
407
  invoke(input: I & Message, options?: Partial<AgentInvokeOptions>): Promise<AgentResponse<O>>;
408
+ private processStreamingAndRetry;
408
409
  private callHooks;
409
410
  private mergeDefaultInput;
410
411
  protected invokeSkill<I extends Message, O extends Message>(skill: Agent<I, O>, input: I & Message, options: AgentInvokeOptions): Promise<O>;
@@ -582,12 +583,14 @@ export interface AgentHooks<I extends Message = Message, O extends Message = Mes
582
583
  error: Error;
583
584
  }, "output", "error">) => PromiseOrValue<void | {
584
585
  output?: O;
586
+ retry?: boolean;
585
587
  }>) | Agent<XOr<{
586
588
  input: I;
587
589
  output: O;
588
590
  error: Error;
589
591
  }, "output", "error">, {
590
592
  output?: O;
593
+ retry?: boolean;
591
594
  }>;
592
595
  onSuccess?: ((event: {
593
596
  context: Context;
@@ -607,9 +610,13 @@ export interface AgentHooks<I extends Message = Message, O extends Message = Mes
607
610
  agent: Agent;
608
611
  input: I;
609
612
  error: Error;
610
- }) => void) | Agent<{
613
+ }) => PromiseOrValue<void | {
614
+ retry?: boolean;
615
+ }>) | Agent<{
611
616
  input: I;
612
617
  error: Error;
618
+ }, {
619
+ retry?: boolean;
613
620
  }>;
614
621
  /**
615
622
  * Called before a skill (sub-agent) is invoked
@@ -1,8 +1,9 @@
1
1
  import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
2
+ import equal from "fast-deep-equal";
2
3
  import nunjucks from "nunjucks";
3
4
  import { ZodObject, z } from "zod";
4
5
  import { logger } from "../utils/logger.js";
5
- import { agentResponseStreamToObject, asyncGeneratorToReadableStream, isAsyncGenerator, objectToAgentResponseStream, onAgentResponseStreamEnd, } from "../utils/stream-utils.js";
6
+ import { agentResponseStreamToObject, asyncGeneratorToReadableStream, isAsyncGenerator, mergeAgentResponseChunk, objectToAgentResponseStream, onAgentResponseStreamEnd, } from "../utils/stream-utils.js";
6
7
  import { checkArguments, createAccessorArray, flat, isEmpty, isNil, isRecord, } from "../utils/type-utils.js";
7
8
  import { replaceTransferAgentToName, transferToAgentOutput, } from "./types.js";
8
9
  export * from "./types.js";
@@ -151,10 +152,13 @@ export class Agent {
151
152
  */
152
153
  description;
153
154
  taskTitle;
154
- renderTaskTitle(input) {
155
+ async renderTaskTitle(input) {
155
156
  if (!this.taskTitle)
156
157
  return;
157
- return nunjucks.renderString(this.taskTitle, { ...input });
158
+ const s = typeof this.taskTitle === "function" ? await this.taskTitle(input) : this.taskTitle;
159
+ if (!s)
160
+ return;
161
+ return nunjucks.renderString(s, { ...input });
158
162
  }
159
163
  _inputSchema;
160
164
  defaultInput;
@@ -312,7 +316,6 @@ export class Agent {
312
316
  if (!this.disableEvents)
313
317
  opts.context.emit("agentStarted", { agent: this, input });
314
318
  try {
315
- let response;
316
319
  const s = await this.callHooks("onStart", { input }, opts);
317
320
  if (s?.input)
318
321
  input = s.input;
@@ -321,31 +324,47 @@ export class Agent {
321
324
  input = checkArguments(`Agent ${this.name} input`, this.inputSchema, input);
322
325
  await this.preprocess(input, opts);
323
326
  this.checkContextStatus(opts);
324
- if (!response) {
325
- response = await this.process(input, opts);
326
- if (response instanceof Agent) {
327
- response = transferToAgentOutput(response);
327
+ const response = asyncGeneratorToReadableStream(this.processStreamingAndRetry(input, opts));
328
+ return await this.checkResponseByGuideRails(input, options.streaming ? response : await agentProcessResultToObject(response), opts);
329
+ }
330
+ catch (error) {
331
+ throw (await this.processAgentError(input, error, opts)).error ?? error;
332
+ }
333
+ }
334
+ async *processStreamingAndRetry(input, options) {
335
+ let output = {};
336
+ for (;;) {
337
+ // Reset output to avoid accumulating old data
338
+ const resetOutput = Object.fromEntries(Object.entries(output).map(([key]) => [key, null]));
339
+ if (!isEmpty(resetOutput)) {
340
+ yield { delta: { json: resetOutput } };
341
+ output = {};
342
+ }
343
+ let response = await this.process(input, options);
344
+ if (response instanceof Agent)
345
+ response = transferToAgentOutput(response);
346
+ const stream = response instanceof ReadableStream
347
+ ? response
348
+ : isAsyncGenerator(response)
349
+ ? asyncGeneratorToReadableStream(response)
350
+ : objectToAgentResponseStream(response);
351
+ try {
352
+ for await (const chunk of stream) {
353
+ mergeAgentResponseChunk(output, chunk);
354
+ yield chunk;
355
+ }
356
+ const result = await this.processAgentOutput(input, output, options);
357
+ if (result && !equal(result, output)) {
358
+ yield { delta: { json: result } };
328
359
  }
360
+ // Close the stream after processing
361
+ break;
329
362
  }
330
- if (opts.streaming) {
331
- const stream = response instanceof ReadableStream
332
- ? response
333
- : isAsyncGenerator(response)
334
- ? asyncGeneratorToReadableStream(response)
335
- : objectToAgentResponseStream(response);
336
- return this.checkResponseByGuideRails(input, onAgentResponseStreamEnd(stream, {
337
- onResult: async (result) => {
338
- return await this.processAgentOutput(input, result, opts);
339
- },
340
- onError: async (error) => {
341
- return await this.processAgentError(input, error, opts);
342
- },
343
- }), opts);
363
+ catch (error) {
364
+ const res = await this.processAgentError(input, error, options);
365
+ if (!res.retry)
366
+ throw res.error ?? error;
344
367
  }
345
- return await this.checkResponseByGuideRails(input, this.processAgentOutput(input, await agentProcessResultToObject(response), opts), opts);
346
- }
347
- catch (error) {
348
- throw await this.processAgentError(input, error, opts);
349
368
  }
350
369
  }
351
370
  async callHooks(hook, input, options) {
@@ -432,12 +451,15 @@ export class Agent {
432
451
  * @param options Invocation options
433
452
  */
434
453
  async processAgentError(input, error, options) {
454
+ if ("$error_has_been_processed" in error && error.$error_has_been_processed)
455
+ return {};
456
+ Object.defineProperty(error, "$error_has_been_processed", { value: true, enumerable: false });
435
457
  logger.error("Invoke agent %s failed with error: %O", this.name, error);
436
458
  if (!this.disableEvents)
437
459
  options.context.emit("agentFailed", { agent: this, error });
438
- await this.callHooks("onError", { input, error }, options);
439
- await this.callHooks("onEnd", { input, error }, options);
440
- return error;
460
+ const res = (await this.callHooks("onError", { input, error }, options)) ?? {};
461
+ Object.assign(res, await this.callHooks("onEnd", { input, error }, options));
462
+ return { ...res };
441
463
  }
442
464
  /**
443
465
  * Check agent invocation usage to prevent exceeding limits
@@ -11,6 +11,7 @@ export interface LoadOptions {
11
11
  new (parameters?: MemoryAgentOptions): MemoryAgent;
12
12
  }[];
13
13
  path: string;
14
+ model?: ChatModel;
14
15
  }
15
16
  export declare function load(options: LoadOptions): Promise<AIGNEOptions>;
16
17
  export declare function loadAgent(path: string, options?: LoadOptions, agentOptions?: AgentOptions): Promise<Agent>;
@@ -20,7 +20,7 @@ export async function load(options) {
20
20
  return {
21
21
  ...aigne,
22
22
  rootDir,
23
- model: await options.loadModel(aigne.model),
23
+ model: options?.model || (await options.loadModel(aigne.model)),
24
24
  agents: pickAgents(aigne.agents ?? []),
25
25
  skills: pickAgents(aigne.skills ?? []),
26
26
  mcpServer: {
@@ -125,8 +125,8 @@ export class AgentResponseProgressStream extends ReadableStream {
125
125
  context.off("agentFailed", onAgentFailed);
126
126
  controller.close();
127
127
  };
128
- const onAgentStarted = (event) => {
129
- const taskTitle = event.agent.renderTaskTitle(event.input);
128
+ const onAgentStarted = async (event) => {
129
+ const taskTitle = await event.agent.renderTaskTitle(event.input);
130
130
  writeEvent("agentStarted", { ...event, taskTitle });
131
131
  };
132
132
  const onAgentSucceed = (event) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/core",
3
- "version": "1.47.0",
3
+ "version": "1.49.0",
4
4
  "description": "The functional core of agentic AI",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -90,7 +90,7 @@
90
90
  "zod": "^3.25.67",
91
91
  "zod-to-json-schema": "^3.24.6",
92
92
  "@aigne/observability-api": "^0.9.0",
93
- "@aigne/platform-helpers": "^0.6.0"
93
+ "@aigne/platform-helpers": "^0.6.1"
94
94
  },
95
95
  "devDependencies": {
96
96
  "@types/bun": "^1.2.18",