@aigne/core 1.49.1 → 1.50.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,25 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.50.0](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.49.1...core-v1.50.0) (2025-08-14)
4
+
5
+
6
+ ### Features
7
+
8
+ * **core:** add priority support for agent hooks ([#358](https://github.com/AIGNE-io/aigne-framework/issues/358)) ([9196141](https://github.com/AIGNE-io/aigne-framework/commit/91961413aea171048a6afae87ffc8dc53e20fca8))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **cli:** log only once in loadAIGNE ([#357](https://github.com/AIGNE-io/aigne-framework/issues/357)) ([6e6d968](https://github.com/AIGNE-io/aigne-framework/commit/6e6d96814fbc87f210522ae16daf94c1f84f311a))
14
+ * **cli:** prevent multiple simultaneous buy credits prompts ([#363](https://github.com/AIGNE-io/aigne-framework/issues/363)) ([b8fb459](https://github.com/AIGNE-io/aigne-framework/commit/b8fb459261fe327bcc9bfb4d163e66863cb797ec))
15
+
16
+
17
+ ### Dependencies
18
+
19
+ * The following workspace dependencies were updated
20
+ * dependencies
21
+ * @aigne/platform-helpers bumped to 0.6.2
22
+
3
23
  ## [1.49.1](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.49.0...core-v1.49.1) (2025-08-12)
4
24
 
5
25
 
@@ -545,6 +545,7 @@ export type AgentOutput<T extends Agent> = T extends Agent<any, infer O> ? O : n
545
545
  * tracing, error handling, and more.
546
546
  */
547
547
  export interface AgentHooks<I extends Message = Message, O extends Message = Message> {
548
+ priority?: "low" | "medium" | "high";
548
549
  /**
549
550
  * Called when agent processing begins
550
551
  *
@@ -50,6 +50,7 @@ const index_js_1 = require("@aigne/platform-helpers/nodejs/index.js");
50
50
  const fast_deep_equal_1 = __importDefault(require("fast-deep-equal"));
51
51
  const nunjucks_1 = __importDefault(require("nunjucks"));
52
52
  const zod_1 = require("zod");
53
+ const agent_utils_js_1 = require("../utils/agent-utils.js");
53
54
  const logger_js_1 = require("../utils/logger.js");
54
55
  const stream_utils_js_1 = require("../utils/stream-utils.js");
55
56
  const type_utils_js_1 = require("../utils/type-utils.js");
@@ -388,15 +389,15 @@ class Agent {
388
389
  yield { delta: { json: resetOutput } };
389
390
  output = {};
390
391
  }
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
392
  try {
393
+ let response = await this.process(input, options);
394
+ if (response instanceof Agent)
395
+ response = (0, types_js_1.transferToAgentOutput)(response);
396
+ const stream = response instanceof ReadableStream
397
+ ? response
398
+ : (0, stream_utils_js_1.isAsyncGenerator)(response)
399
+ ? (0, stream_utils_js_1.asyncGeneratorToReadableStream)(response)
400
+ : (0, stream_utils_js_1.objectToAgentResponseStream)(response);
400
401
  for await (const chunk of stream) {
401
402
  (0, stream_utils_js_1.mergeAgentResponseChunk)(output, chunk);
402
403
  yield chunk;
@@ -418,16 +419,20 @@ class Agent {
418
419
  async callHooks(hook, input, options) {
419
420
  const { context } = options;
420
421
  const result = {};
421
- for (const hooks of (0, type_utils_js_1.flat)(options.hooks, options.context.hooks, this.hooks)) {
422
- const h = hooks[hook];
423
- if (!h)
424
- continue;
422
+ const hs = (0, type_utils_js_1.flat)(hook);
423
+ const hooks = (0, agent_utils_js_1.sortHooks)((0, type_utils_js_1.flat)(options.hooks, options.context.hooks, this.hooks))
424
+ .flatMap((hooks) => hs.map((h) => hooks[h]))
425
+ .filter(type_utils_js_1.isNonNullable);
426
+ for (const h of hooks) {
425
427
  if (typeof h === "function") {
426
428
  Object.assign(result, await h({ ...input, context, agent: this }));
427
429
  }
428
430
  else {
429
431
  Object.assign(result, await context.invoke(h, { ...input, agent: this }));
430
432
  }
433
+ // If the hook returns a retry signal, we stop processing further hooks
434
+ if ("retry" in result && result.retry)
435
+ return result;
431
436
  }
432
437
  return result;
433
438
  }
@@ -480,10 +485,7 @@ class Agent {
480
485
  let finalOutput = this.includeInputInOutput ? { ...input, ...parsedOutput } : parsedOutput;
481
486
  await this.postprocess(input, finalOutput, options);
482
487
  logger_js_1.logger.debug("Invoke agent %s succeed with output: %O", this.name, finalOutput);
483
- let o = await this.callHooks("onSuccess", { input, output: finalOutput }, options);
484
- if (o?.output)
485
- finalOutput = o.output;
486
- o = await this.callHooks("onEnd", { input, output: finalOutput }, options);
488
+ const o = await this.callHooks(["onSuccess", "onEnd"], { input, output: finalOutput }, options);
487
489
  if (o?.output)
488
490
  finalOutput = o.output;
489
491
  if (!this.disableEvents)
@@ -505,8 +507,7 @@ class Agent {
505
507
  logger_js_1.logger.error("Invoke agent %s failed with error: %O", this.name, error);
506
508
  if (!this.disableEvents)
507
509
  options.context.emit("agentFailed", { agent: this, error });
508
- const res = (await this.callHooks("onError", { input, error }, options)) ?? {};
509
- Object.assign(res, await this.callHooks("onEnd", { input, error }, options));
510
+ const res = (await this.callHooks(["onError", "onEnd"], { input, error }, options)) ?? {};
510
511
  return { ...res };
511
512
  }
512
513
  /**
@@ -26,6 +26,11 @@ import { Agent, type AgentInvokeOptions, type AgentProcessResult, type Message }
26
26
  export declare abstract class ChatModel extends Agent<ChatModelInput, ChatModelOutput> {
27
27
  tag: string;
28
28
  constructor();
29
+ abstract getCredential(): {
30
+ url?: string;
31
+ apiKey?: string;
32
+ model?: string;
33
+ };
29
34
  /**
30
35
  * Indicates whether the model supports parallel tool calls
31
36
  *
@@ -1,8 +1,9 @@
1
1
  import { type ZodType } from "zod";
2
- import type { FunctionAgentFn } from "../agents/agent.js";
2
+ import type { AgentHooks, FunctionAgentFn } from "../agents/agent.js";
3
3
  import { AIAgentToolChoice } from "../agents/ai-agent.js";
4
4
  import { ProcessMode, type ReflectionMode } from "../agents/team-agent.js";
5
5
  export interface HooksSchema {
6
+ priority?: AgentHooks["priority"];
6
7
  onStart?: NestAgentSchema;
7
8
  onSuccess?: NestAgentSchema;
8
9
  onError?: NestAgentSchema;
@@ -22,6 +22,7 @@ async function parseAgentFile(path, data) {
22
22
  })),
23
23
  ]));
24
24
  const hooksSchema = (0, schema_js_1.camelizeSchema)(zod_1.z.object({
25
+ priority: (0, schema_js_1.optionalize)(zod_1.z.union([zod_1.z.literal("low"), zod_1.z.literal("medium"), zod_1.z.literal("high")])),
25
26
  onStart: (0, schema_js_1.optionalize)(nestAgentSchema),
26
27
  onSuccess: (0, schema_js_1.optionalize)(nestAgentSchema),
27
28
  onError: (0, schema_js_1.optionalize)(nestAgentSchema),
@@ -64,6 +64,7 @@ async function parseHooks(path, hooks, options) {
64
64
  if (!hooks.length)
65
65
  return undefined;
66
66
  return await Promise.all(hooks.map(async (hook) => ({
67
+ priority: hook.priority,
67
68
  onStart: hook.onStart ? await loadNestAgent(path, hook.onStart, options) : undefined,
68
69
  onSuccess: hook.onSuccess ? await loadNestAgent(path, hook.onSuccess, options) : undefined,
69
70
  onError: hook.onError ? await loadNestAgent(path, hook.onError, options) : undefined,
@@ -0,0 +1,2 @@
1
+ import type { AgentHooks } from "../agents/agent.ts";
2
+ export declare function sortHooks(hooks: AgentHooks[]): AgentHooks[];
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sortHooks = sortHooks;
4
+ const priorities = ["high", "medium", "low"];
5
+ function sortHooks(hooks) {
6
+ return hooks
7
+ .slice(0)
8
+ .sort(({ priority: a = "low" }, { priority: b = "low" }) => a === b ? 0 : priorities.indexOf(a) - priorities.indexOf(b));
9
+ }
@@ -545,6 +545,7 @@ export type AgentOutput<T extends Agent> = T extends Agent<any, infer O> ? O : n
545
545
  * tracing, error handling, and more.
546
546
  */
547
547
  export interface AgentHooks<I extends Message = Message, O extends Message = Message> {
548
+ priority?: "low" | "medium" | "high";
548
549
  /**
549
550
  * Called when agent processing begins
550
551
  *
@@ -26,6 +26,11 @@ import { Agent, type AgentInvokeOptions, type AgentProcessResult, type Message }
26
26
  export declare abstract class ChatModel extends Agent<ChatModelInput, ChatModelOutput> {
27
27
  tag: string;
28
28
  constructor();
29
+ abstract getCredential(): {
30
+ url?: string;
31
+ apiKey?: string;
32
+ model?: string;
33
+ };
29
34
  /**
30
35
  * Indicates whether the model supports parallel tool calls
31
36
  *
@@ -1,8 +1,9 @@
1
1
  import { type ZodType } from "zod";
2
- import type { FunctionAgentFn } from "../agents/agent.js";
2
+ import type { AgentHooks, FunctionAgentFn } from "../agents/agent.js";
3
3
  import { AIAgentToolChoice } from "../agents/ai-agent.js";
4
4
  import { ProcessMode, type ReflectionMode } from "../agents/team-agent.js";
5
5
  export interface HooksSchema {
6
+ priority?: AgentHooks["priority"];
6
7
  onStart?: NestAgentSchema;
7
8
  onSuccess?: NestAgentSchema;
8
9
  onError?: NestAgentSchema;
@@ -0,0 +1,2 @@
1
+ import type { AgentHooks } from "../agents/agent.ts";
2
+ export declare function sortHooks(hooks: AgentHooks[]): AgentHooks[];
@@ -545,6 +545,7 @@ export type AgentOutput<T extends Agent> = T extends Agent<any, infer O> ? O : n
545
545
  * tracing, error handling, and more.
546
546
  */
547
547
  export interface AgentHooks<I extends Message = Message, O extends Message = Message> {
548
+ priority?: "low" | "medium" | "high";
548
549
  /**
549
550
  * Called when agent processing begins
550
551
  *
@@ -2,9 +2,10 @@ import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
2
2
  import equal from "fast-deep-equal";
3
3
  import nunjucks from "nunjucks";
4
4
  import { ZodObject, z } from "zod";
5
+ import { sortHooks } from "../utils/agent-utils.js";
5
6
  import { logger } from "../utils/logger.js";
6
7
  import { agentResponseStreamToObject, asyncGeneratorToReadableStream, isAsyncGenerator, mergeAgentResponseChunk, objectToAgentResponseStream, onAgentResponseStreamEnd, } from "../utils/stream-utils.js";
7
- import { checkArguments, createAccessorArray, flat, isEmpty, isNil, isRecord, } from "../utils/type-utils.js";
8
+ import { checkArguments, createAccessorArray, flat, isEmpty, isNil, isNonNullable, isRecord, } from "../utils/type-utils.js";
8
9
  import { replaceTransferAgentToName, transferToAgentOutput, } from "./types.js";
9
10
  export * from "./types.js";
10
11
  export const DEFAULT_INPUT_ACTION_GET = "$get";
@@ -340,15 +341,15 @@ export class Agent {
340
341
  yield { delta: { json: resetOutput } };
341
342
  output = {};
342
343
  }
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
344
  try {
345
+ let response = await this.process(input, options);
346
+ if (response instanceof Agent)
347
+ response = transferToAgentOutput(response);
348
+ const stream = response instanceof ReadableStream
349
+ ? response
350
+ : isAsyncGenerator(response)
351
+ ? asyncGeneratorToReadableStream(response)
352
+ : objectToAgentResponseStream(response);
352
353
  for await (const chunk of stream) {
353
354
  mergeAgentResponseChunk(output, chunk);
354
355
  yield chunk;
@@ -370,16 +371,20 @@ export class Agent {
370
371
  async callHooks(hook, input, options) {
371
372
  const { context } = options;
372
373
  const result = {};
373
- for (const hooks of flat(options.hooks, options.context.hooks, this.hooks)) {
374
- const h = hooks[hook];
375
- if (!h)
376
- continue;
374
+ const hs = flat(hook);
375
+ const hooks = sortHooks(flat(options.hooks, options.context.hooks, this.hooks))
376
+ .flatMap((hooks) => hs.map((h) => hooks[h]))
377
+ .filter(isNonNullable);
378
+ for (const h of hooks) {
377
379
  if (typeof h === "function") {
378
380
  Object.assign(result, await h({ ...input, context, agent: this }));
379
381
  }
380
382
  else {
381
383
  Object.assign(result, await context.invoke(h, { ...input, agent: this }));
382
384
  }
385
+ // If the hook returns a retry signal, we stop processing further hooks
386
+ if ("retry" in result && result.retry)
387
+ return result;
383
388
  }
384
389
  return result;
385
390
  }
@@ -432,10 +437,7 @@ export class Agent {
432
437
  let finalOutput = this.includeInputInOutput ? { ...input, ...parsedOutput } : parsedOutput;
433
438
  await this.postprocess(input, finalOutput, options);
434
439
  logger.debug("Invoke agent %s succeed with output: %O", this.name, finalOutput);
435
- let o = await this.callHooks("onSuccess", { input, output: finalOutput }, options);
436
- if (o?.output)
437
- finalOutput = o.output;
438
- o = await this.callHooks("onEnd", { input, output: finalOutput }, options);
440
+ const o = await this.callHooks(["onSuccess", "onEnd"], { input, output: finalOutput }, options);
439
441
  if (o?.output)
440
442
  finalOutput = o.output;
441
443
  if (!this.disableEvents)
@@ -457,8 +459,7 @@ export class Agent {
457
459
  logger.error("Invoke agent %s failed with error: %O", this.name, error);
458
460
  if (!this.disableEvents)
459
461
  options.context.emit("agentFailed", { agent: this, error });
460
- const res = (await this.callHooks("onError", { input, error }, options)) ?? {};
461
- Object.assign(res, await this.callHooks("onEnd", { input, error }, options));
462
+ const res = (await this.callHooks(["onError", "onEnd"], { input, error }, options)) ?? {};
462
463
  return { ...res };
463
464
  }
464
465
  /**
@@ -26,6 +26,11 @@ import { Agent, type AgentInvokeOptions, type AgentProcessResult, type Message }
26
26
  export declare abstract class ChatModel extends Agent<ChatModelInput, ChatModelOutput> {
27
27
  tag: string;
28
28
  constructor();
29
+ abstract getCredential(): {
30
+ url?: string;
31
+ apiKey?: string;
32
+ model?: string;
33
+ };
29
34
  /**
30
35
  * Indicates whether the model supports parallel tool calls
31
36
  *
@@ -1,8 +1,9 @@
1
1
  import { type ZodType } from "zod";
2
- import type { FunctionAgentFn } from "../agents/agent.js";
2
+ import type { AgentHooks, FunctionAgentFn } from "../agents/agent.js";
3
3
  import { AIAgentToolChoice } from "../agents/ai-agent.js";
4
4
  import { ProcessMode, type ReflectionMode } from "../agents/team-agent.js";
5
5
  export interface HooksSchema {
6
+ priority?: AgentHooks["priority"];
6
7
  onStart?: NestAgentSchema;
7
8
  onSuccess?: NestAgentSchema;
8
9
  onError?: NestAgentSchema;
@@ -18,6 +18,7 @@ export async function parseAgentFile(path, data) {
18
18
  })),
19
19
  ]));
20
20
  const hooksSchema = camelizeSchema(z.object({
21
+ priority: optionalize(z.union([z.literal("low"), z.literal("medium"), z.literal("high")])),
21
22
  onStart: optionalize(nestAgentSchema),
22
23
  onSuccess: optionalize(nestAgentSchema),
23
24
  onError: optionalize(nestAgentSchema),
@@ -59,6 +59,7 @@ async function parseHooks(path, hooks, options) {
59
59
  if (!hooks.length)
60
60
  return undefined;
61
61
  return await Promise.all(hooks.map(async (hook) => ({
62
+ priority: hook.priority,
62
63
  onStart: hook.onStart ? await loadNestAgent(path, hook.onStart, options) : undefined,
63
64
  onSuccess: hook.onSuccess ? await loadNestAgent(path, hook.onSuccess, options) : undefined,
64
65
  onError: hook.onError ? await loadNestAgent(path, hook.onError, options) : undefined,
@@ -0,0 +1,2 @@
1
+ import type { AgentHooks } from "../agents/agent.ts";
2
+ export declare function sortHooks(hooks: AgentHooks[]): AgentHooks[];
@@ -0,0 +1,6 @@
1
+ const priorities = ["high", "medium", "low"];
2
+ export function sortHooks(hooks) {
3
+ return hooks
4
+ .slice(0)
5
+ .sort(({ priority: a = "low" }, { priority: b = "low" }) => a === b ? 0 : priorities.indexOf(a) - priorities.indexOf(b));
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/core",
3
- "version": "1.49.1",
3
+ "version": "1.50.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.1"
93
+ "@aigne/platform-helpers": "^0.6.2"
94
94
  },
95
95
  "devDependencies": {
96
96
  "@types/bun": "^1.2.18",