@aigne/core 1.48.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,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.49.0](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.48.0...core-v1.49.0) (2025-08-12)
4
+
5
+
6
+ ### Features
7
+
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))
14
+
15
+
16
+ ### Dependencies
17
+
18
+ * The following workspace dependencies were updated
19
+ * dependencies
20
+ * @aigne/platform-helpers bumped to 0.6.1
21
+
3
22
  ## [1.48.0](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.47.0...core-v1.48.0) (2025-08-12)
4
23
 
5
24
 
@@ -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");
@@ -363,7 +364,6 @@ class Agent {
363
364
  if (!this.disableEvents)
364
365
  opts.context.emit("agentStarted", { agent: this, input });
365
366
  try {
366
- let response;
367
367
  const s = await this.callHooks("onStart", { input }, opts);
368
368
  if (s?.input)
369
369
  input = s.input;
@@ -372,31 +372,47 @@ class Agent {
372
372
  input = (0, type_utils_js_1.checkArguments)(`Agent ${this.name} input`, this.inputSchema, input);
373
373
  await this.preprocess(input, opts);
374
374
  this.checkContextStatus(opts);
375
- if (!response) {
376
- response = await this.process(input, opts);
377
- if (response instanceof Agent) {
378
- 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;
379
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 } };
407
+ }
408
+ // Close the stream after processing
409
+ break;
380
410
  }
381
- if (opts.streaming) {
382
- const stream = response instanceof ReadableStream
383
- ? response
384
- : (0, stream_utils_js_1.isAsyncGenerator)(response)
385
- ? (0, stream_utils_js_1.asyncGeneratorToReadableStream)(response)
386
- : (0, stream_utils_js_1.objectToAgentResponseStream)(response);
387
- return this.checkResponseByGuideRails(input, (0, stream_utils_js_1.onAgentResponseStreamEnd)(stream, {
388
- onResult: async (result) => {
389
- return await this.processAgentOutput(input, result, opts);
390
- },
391
- onError: async (error) => {
392
- return await this.processAgentError(input, error, opts);
393
- },
394
- }), opts);
411
+ catch (error) {
412
+ const res = await this.processAgentError(input, error, options);
413
+ if (!res.retry)
414
+ throw res.error ?? error;
395
415
  }
396
- return await this.checkResponseByGuideRails(input, this.processAgentOutput(input, await agentProcessResultToObject(response), opts), opts);
397
- }
398
- catch (error) {
399
- throw await this.processAgentError(input, error, opts);
400
416
  }
401
417
  }
402
418
  async callHooks(hook, input, options) {
@@ -483,12 +499,15 @@ class Agent {
483
499
  * @param options Invocation options
484
500
  */
485
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 });
486
505
  logger_js_1.logger.error("Invoke agent %s failed with error: %O", this.name, error);
487
506
  if (!this.disableEvents)
488
507
  options.context.emit("agentFailed", { agent: this, error });
489
- await this.callHooks("onError", { input, error }, options);
490
- await this.callHooks("onEnd", { input, error }, options);
491
- 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 };
492
511
  }
493
512
  /**
494
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: {
@@ -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>;
@@ -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";
@@ -315,7 +316,6 @@ export class Agent {
315
316
  if (!this.disableEvents)
316
317
  opts.context.emit("agentStarted", { agent: this, input });
317
318
  try {
318
- let response;
319
319
  const s = await this.callHooks("onStart", { input }, opts);
320
320
  if (s?.input)
321
321
  input = s.input;
@@ -324,31 +324,47 @@ export class Agent {
324
324
  input = checkArguments(`Agent ${this.name} input`, this.inputSchema, input);
325
325
  await this.preprocess(input, opts);
326
326
  this.checkContextStatus(opts);
327
- if (!response) {
328
- response = await this.process(input, opts);
329
- if (response instanceof Agent) {
330
- 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;
331
355
  }
356
+ const result = await this.processAgentOutput(input, output, options);
357
+ if (result && !equal(result, output)) {
358
+ yield { delta: { json: result } };
359
+ }
360
+ // Close the stream after processing
361
+ break;
332
362
  }
333
- if (opts.streaming) {
334
- const stream = response instanceof ReadableStream
335
- ? response
336
- : isAsyncGenerator(response)
337
- ? asyncGeneratorToReadableStream(response)
338
- : objectToAgentResponseStream(response);
339
- return this.checkResponseByGuideRails(input, onAgentResponseStreamEnd(stream, {
340
- onResult: async (result) => {
341
- return await this.processAgentOutput(input, result, opts);
342
- },
343
- onError: async (error) => {
344
- return await this.processAgentError(input, error, opts);
345
- },
346
- }), opts);
363
+ catch (error) {
364
+ const res = await this.processAgentError(input, error, options);
365
+ if (!res.retry)
366
+ throw res.error ?? error;
347
367
  }
348
- return await this.checkResponseByGuideRails(input, this.processAgentOutput(input, await agentProcessResultToObject(response), opts), opts);
349
- }
350
- catch (error) {
351
- throw await this.processAgentError(input, error, opts);
352
368
  }
353
369
  }
354
370
  async callHooks(hook, input, options) {
@@ -435,12 +451,15 @@ export class Agent {
435
451
  * @param options Invocation options
436
452
  */
437
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 });
438
457
  logger.error("Invoke agent %s failed with error: %O", this.name, error);
439
458
  if (!this.disableEvents)
440
459
  options.context.emit("agentFailed", { agent: this, error });
441
- await this.callHooks("onError", { input, error }, options);
442
- await this.callHooks("onEnd", { input, error }, options);
443
- 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 };
444
463
  }
445
464
  /**
446
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: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/core",
3
- "version": "1.48.0",
3
+ "version": "1.49.0",
4
4
  "description": "The functional core of agentic AI",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -89,8 +89,8 @@
89
89
  "yaml": "^2.8.0",
90
90
  "zod": "^3.25.67",
91
91
  "zod-to-json-schema": "^3.24.6",
92
- "@aigne/platform-helpers": "^0.6.0",
93
- "@aigne/observability-api": "^0.9.0"
92
+ "@aigne/observability-api": "^0.9.0",
93
+ "@aigne/platform-helpers": "^0.6.1"
94
94
  },
95
95
  "devDependencies": {
96
96
  "@types/bun": "^1.2.18",