@aigne/core 1.28.2 → 1.29.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 +7 -0
  2. package/lib/cjs/agents/agent.d.ts +1 -0
  3. package/lib/cjs/agents/agent.js +9 -5
  4. package/lib/cjs/agents/team-agent.d.ts +59 -2
  5. package/lib/cjs/agents/team-agent.js +54 -0
  6. package/lib/cjs/agents/transform-agent.d.ts +82 -0
  7. package/lib/cjs/agents/transform-agent.js +67 -0
  8. package/lib/cjs/index.d.ts +1 -0
  9. package/lib/cjs/index.js +1 -0
  10. package/lib/cjs/loader/agent-js.d.ts +2 -2
  11. package/lib/cjs/loader/agent-js.js +11 -15
  12. package/lib/cjs/loader/agent-yaml.d.ts +35 -44
  13. package/lib/cjs/loader/agent-yaml.js +58 -103
  14. package/lib/cjs/loader/index.js +28 -11
  15. package/lib/cjs/loader/schema.d.ts +2 -1
  16. package/lib/cjs/loader/schema.js +4 -0
  17. package/lib/dts/agents/agent.d.ts +1 -0
  18. package/lib/dts/agents/team-agent.d.ts +59 -2
  19. package/lib/dts/agents/transform-agent.d.ts +82 -0
  20. package/lib/dts/index.d.ts +1 -0
  21. package/lib/dts/loader/agent-js.d.ts +2 -2
  22. package/lib/dts/loader/agent-yaml.d.ts +35 -44
  23. package/lib/dts/loader/schema.d.ts +2 -1
  24. package/lib/esm/agents/agent.d.ts +1 -0
  25. package/lib/esm/agents/agent.js +8 -5
  26. package/lib/esm/agents/team-agent.d.ts +59 -2
  27. package/lib/esm/agents/team-agent.js +53 -2
  28. package/lib/esm/agents/transform-agent.d.ts +82 -0
  29. package/lib/esm/agents/transform-agent.js +60 -0
  30. package/lib/esm/index.d.ts +1 -0
  31. package/lib/esm/index.js +1 -0
  32. package/lib/esm/loader/agent-js.d.ts +2 -2
  33. package/lib/esm/loader/agent-js.js +9 -16
  34. package/lib/esm/loader/agent-yaml.d.ts +35 -44
  35. package/lib/esm/loader/agent-yaml.js +56 -104
  36. package/lib/esm/loader/index.js +28 -11
  37. package/lib/esm/loader/schema.d.ts +2 -1
  38. package/lib/esm/loader/schema.js +3 -0
  39. package/package.json +2 -1
  40. package/lib/cjs/utils/camelize.d.ts +0 -13
  41. package/lib/cjs/utils/camelize.js +0 -16
  42. package/lib/dts/utils/camelize.d.ts +0 -13
  43. package/lib/esm/utils/camelize.d.ts +0 -13
  44. package/lib/esm/utils/camelize.js +0 -10
@@ -33,6 +33,42 @@ export interface TeamAgentOptions<I extends Message, O extends Message> extends
33
33
  * @default {ProcessMode.sequential}
34
34
  */
35
35
  mode?: ProcessMode;
36
+ /**
37
+ * Specifies which input field should be treated as an array for iterative processing.
38
+ *
39
+ * When this property is set, the TeamAgent will iterate over the array values in the
40
+ * specified input field, processing each element individually through the team's agents.
41
+ * The results from each iteration are accumulated and returned as a streaming response.
42
+ *
43
+ * This is particularly useful for batch processing scenarios where you need to apply
44
+ * the same agent workflow to multiple similar data items.
45
+ *
46
+ * @remarks
47
+ * - The specified field must contain an array or array-like value
48
+ * - Each array element should be an object that can be merged with the base input
49
+ * - Non-array values will be treated as single-element arrays
50
+ * - The processing results are streamed incrementally as each iteration completes
51
+ */
52
+ iterateOn?: keyof I;
53
+ /**
54
+ * Controls whether to merge the output from each iteration back into the array items
55
+ * for subsequent iterations when using `iterateOn`.
56
+ *
57
+ * When set to `true`, the output from processing each array element is merged back
58
+ * into that element, making it available for the next iteration. This creates a
59
+ * cumulative effect where each iteration builds upon the results of previous ones.
60
+ *
61
+ * When set to `false` or undefined, each array element is processed independently
62
+ * without any cross-iteration data sharing.
63
+ *
64
+ * This is particularly useful for scenarios where:
65
+ * - You need to progressively enrich data across iterations
66
+ * - Later iterations depend on the results of earlier ones
67
+ * - You want to build a chain of transformations on array data
68
+ *
69
+ * @default false
70
+ */
71
+ iterateWithPreviousOutput?: boolean;
36
72
  }
37
73
  /**
38
74
  * TeamAgent coordinates a group of agents working together to accomplish tasks.
@@ -80,6 +116,25 @@ export declare class TeamAgent<I extends Message, O extends Message> extends Age
80
116
  * This can be either sequential (one after another) or parallel (all at once).
81
117
  */
82
118
  mode: ProcessMode;
119
+ /**
120
+ * The input field key to iterate over when processing array inputs.
121
+ *
122
+ * When set, this property enables the TeamAgent to process array values iteratively,
123
+ * where each array element is processed individually through the team's agent workflow.
124
+ * The accumulated results are returned via streaming response chunks.
125
+ *
126
+ * @see TeamAgentOptions.iterateOn for detailed documentation
127
+ */
128
+ iterateOn?: keyof I;
129
+ /**
130
+ * Controls whether to merge the output from each iteration back into the array items
131
+ * for subsequent iterations when using `iterateOn`.
132
+ *
133
+ * @see TeamAgentOptions.iterateWithPreviousOutput for detailed documentation
134
+ *
135
+ * @default false
136
+ */
137
+ iterateWithPreviousOutput?: boolean;
83
138
  /**
84
139
  * Process an input message by routing it through the team's agents.
85
140
  *
@@ -93,6 +148,8 @@ export declare class TeamAgent<I extends Message, O extends Message> extends Age
93
148
  * @returns A stream of message chunks that collectively form the response
94
149
  */
95
150
  process(input: I, options: AgentInvokeOptions): PromiseOrValue<AgentProcessResult<O>>;
151
+ private _processIterator;
152
+ private _process;
96
153
  /**
97
154
  * Process input sequentially through each agent in the team.
98
155
  *
@@ -108,7 +165,7 @@ export declare class TeamAgent<I extends Message, O extends Message> extends Age
108
165
  *
109
166
  * @private
110
167
  */
111
- _processSequential(input: I, options: AgentInvokeOptions): PromiseOrValue<AgentProcessResult<O>>;
168
+ _processSequential(input: Message, options: AgentInvokeOptions): PromiseOrValue<AgentProcessResult<O>>;
112
169
  /**
113
170
  * Process input in parallel through all agents in the team.
114
171
  *
@@ -123,5 +180,5 @@ export declare class TeamAgent<I extends Message, O extends Message> extends Age
123
180
  *
124
181
  * @private
125
182
  */
126
- _processParallel(input: I, options: AgentInvokeOptions): PromiseOrValue<AgentProcessResult<O>>;
183
+ _processParallel(input: Message, options: AgentInvokeOptions): PromiseOrValue<AgentProcessResult<O>>;
127
184
  }
@@ -1,6 +1,8 @@
1
+ import assert from "node:assert";
2
+ import { produce } from "immer";
1
3
  import { mergeAgentResponseChunk } from "../utils/stream-utils.js";
2
- import { isEmpty } from "../utils/type-utils.js";
3
- import { Agent, isAgentResponseDelta, } from "./agent.js";
4
+ import { isEmpty, isNil, isRecord, omit } from "../utils/type-utils.js";
5
+ import { Agent, agentProcessResultToObject, isAgentResponseDelta, } from "./agent.js";
4
6
  /**
5
7
  * Defines the processing modes available for a TeamAgent.
6
8
  *
@@ -68,6 +70,8 @@ export class TeamAgent extends Agent {
68
70
  constructor(options) {
69
71
  super(options);
70
72
  this.mode = options.mode ?? ProcessMode.sequential;
73
+ this.iterateOn = options.iterateOn;
74
+ this.iterateWithPreviousOutput = options.iterateWithPreviousOutput;
71
75
  }
72
76
  /**
73
77
  * The processing mode that determines how agents in the team are executed.
@@ -75,6 +79,25 @@ export class TeamAgent extends Agent {
75
79
  * This can be either sequential (one after another) or parallel (all at once).
76
80
  */
77
81
  mode;
82
+ /**
83
+ * The input field key to iterate over when processing array inputs.
84
+ *
85
+ * When set, this property enables the TeamAgent to process array values iteratively,
86
+ * where each array element is processed individually through the team's agent workflow.
87
+ * The accumulated results are returned via streaming response chunks.
88
+ *
89
+ * @see TeamAgentOptions.iterateOn for detailed documentation
90
+ */
91
+ iterateOn;
92
+ /**
93
+ * Controls whether to merge the output from each iteration back into the array items
94
+ * for subsequent iterations when using `iterateOn`.
95
+ *
96
+ * @see TeamAgentOptions.iterateWithPreviousOutput for detailed documentation
97
+ *
98
+ * @default false
99
+ */
100
+ iterateWithPreviousOutput;
78
101
  /**
79
102
  * Process an input message by routing it through the team's agents.
80
103
  *
@@ -88,6 +111,34 @@ export class TeamAgent extends Agent {
88
111
  * @returns A stream of message chunks that collectively form the response
89
112
  */
90
113
  process(input, options) {
114
+ if (this.iterateOn) {
115
+ return this._processIterator(this.iterateOn, input, options);
116
+ }
117
+ return this._process(input, options);
118
+ }
119
+ async *_processIterator(key, input, options) {
120
+ assert(this.iterateOn, "iterateInputKey must be defined for iterator processing");
121
+ let arr = input[this.iterateOn];
122
+ arr = Array.isArray(arr) ? [...arr] : isNil(arr) ? [arr] : [];
123
+ const result = [];
124
+ for (let i = 0; i < arr.length; i++) {
125
+ const item = arr[i];
126
+ if (!isRecord(item))
127
+ throw new TypeError(`Expected ${String(key)} to be an object, got ${typeof item}`);
128
+ const res = await agentProcessResultToObject(await this._process({ ...input, [key]: arr, ...item }, { ...options, streaming: false }));
129
+ // Merge the item result with the original item used for next iteration
130
+ if (this.iterateWithPreviousOutput) {
131
+ arr = produce(arr, (draft) => {
132
+ const item = draft[i];
133
+ assert(item);
134
+ Object.assign(item, res);
135
+ });
136
+ }
137
+ result.push(omit(res, key));
138
+ yield { delta: { json: { [key]: result } } };
139
+ }
140
+ }
141
+ _process(input, options) {
91
142
  switch (this.mode) {
92
143
  case ProcessMode.sequential:
93
144
  return this._processSequential(input, options);
@@ -0,0 +1,82 @@
1
+ import { Agent, type AgentOptions, type Message } from "./agent.js";
2
+ /**
3
+ * Configuration options for TransformAgent
4
+ *
5
+ * TransformAgent is a specialized agent that transforms input data to output data
6
+ * using [JSONata](https://jsonata.org/) expressions. It's particularly useful for:
7
+ * - Data format conversion (e.g., snake_case to camelCase)
8
+ * - Field mapping and renaming
9
+ * - Data structure transformation
10
+ * - Simple data processing without complex logic
11
+ * - API response normalization
12
+ * - Configuration data transformation
13
+ */
14
+ export interface TransformAgentOptions<I extends Message, O extends Message> extends AgentOptions<I, O> {
15
+ /**
16
+ * JSONata expression string for data transformation
17
+ *
18
+ * JSONata is a lightweight query and transformation language for JSON data.
19
+ * The expression defines how input data should be transformed into output data.
20
+ *
21
+ * Common JSONata patterns:
22
+ * - Field mapping: `{ "newField": oldField }`
23
+ * - Array transformation: `items.{ "name": product_name, "price": price }`
24
+ * - Calculations: `$sum(items.price)`, `$count(items)`
25
+ * - Conditional logic: `condition ? value1 : value2`
26
+ * - String operations: `$uppercase(name)`, `$substring(text, 0, 10)`
27
+ *
28
+ * @see https://jsonata.org/ for complete JSONata syntax documentation
29
+ * @see https://try.jsonata.org/ for interactive JSONata playground
30
+ */
31
+ jsonata: string;
32
+ }
33
+ /**
34
+ * TransformAgent - A specialized agent for data transformation using JSONata expressions
35
+ *
36
+ * This agent provides a declarative way to transform structured data without writing
37
+ * custom processing logic. It leverages the power of JSONata, a lightweight query and
38
+ * transformation language, to handle complex data manipulations through simple expressions.
39
+ *
40
+ * Common Use Cases:
41
+ * - API response normalization and field mapping
42
+ * - Database query result transformation
43
+ * - Configuration data restructuring
44
+ * - Data format conversion (snake_case ↔ camelCase)
45
+ * - Aggregation and calculation operations
46
+ * - Filtering and conditional data processing
47
+ */
48
+ export declare class TransformAgent<I extends Message = Message, O extends Message = Message> extends Agent<I, O> {
49
+ static type: string;
50
+ /**
51
+ * Factory method to create a new TransformAgent instance
52
+ *
53
+ * Provides a convenient way to create TransformAgent instances with proper typing
54
+ *
55
+ * @param options Configuration options for the TransformAgent
56
+ * @returns A new TransformAgent instance
57
+ */
58
+ static from<I extends Message, O extends Message>(options: TransformAgentOptions<I, O>): TransformAgent<I, O>;
59
+ /**
60
+ * Create a new TransformAgent instance
61
+ *
62
+ * @param options Configuration options including the JSONata expression
63
+ */
64
+ constructor(options: TransformAgentOptions<I, O>);
65
+ /**
66
+ * The JSONata expression string used for data transformation
67
+ *
68
+ * This expression is compiled and executed against input data to produce
69
+ * the transformed output. The expression is stored as a string and compiled
70
+ * on each invocation for maximum flexibility.
71
+ */
72
+ private jsonata;
73
+ /**
74
+ * Process input data using the configured JSONata expression
75
+ *
76
+ * This method compiles the JSONata expression and evaluates it against the input data.
77
+ *
78
+ * @param input The input message to transform
79
+ * @returns Promise resolving to the transformed output message
80
+ */
81
+ process(input: I): Promise<O>;
82
+ }
@@ -0,0 +1,60 @@
1
+ import jsonata from "jsonata";
2
+ import { Agent } from "./agent.js";
3
+ /**
4
+ * TransformAgent - A specialized agent for data transformation using JSONata expressions
5
+ *
6
+ * This agent provides a declarative way to transform structured data without writing
7
+ * custom processing logic. It leverages the power of JSONata, a lightweight query and
8
+ * transformation language, to handle complex data manipulations through simple expressions.
9
+ *
10
+ * Common Use Cases:
11
+ * - API response normalization and field mapping
12
+ * - Database query result transformation
13
+ * - Configuration data restructuring
14
+ * - Data format conversion (snake_case ↔ camelCase)
15
+ * - Aggregation and calculation operations
16
+ * - Filtering and conditional data processing
17
+ */
18
+ export class TransformAgent extends Agent {
19
+ static type = "TransformAgent";
20
+ /**
21
+ * Factory method to create a new TransformAgent instance
22
+ *
23
+ * Provides a convenient way to create TransformAgent instances with proper typing
24
+ *
25
+ * @param options Configuration options for the TransformAgent
26
+ * @returns A new TransformAgent instance
27
+ */
28
+ static from(options) {
29
+ return new TransformAgent(options);
30
+ }
31
+ /**
32
+ * Create a new TransformAgent instance
33
+ *
34
+ * @param options Configuration options including the JSONata expression
35
+ */
36
+ constructor(options) {
37
+ super(options);
38
+ this.jsonata = options.jsonata;
39
+ }
40
+ /**
41
+ * The JSONata expression string used for data transformation
42
+ *
43
+ * This expression is compiled and executed against input data to produce
44
+ * the transformed output. The expression is stored as a string and compiled
45
+ * on each invocation for maximum flexibility.
46
+ */
47
+ jsonata;
48
+ /**
49
+ * Process input data using the configured JSONata expression
50
+ *
51
+ * This method compiles the JSONata expression and evaluates it against the input data.
52
+ *
53
+ * @param input The input message to transform
54
+ * @returns Promise resolving to the transformed output message
55
+ */
56
+ async process(input) {
57
+ const expression = jsonata(this.jsonata);
58
+ return await expression.evaluate(input);
59
+ }
60
+ }
@@ -4,6 +4,7 @@ export * from "./agents/chat-model.js";
4
4
  export * from "./agents/guide-rail-agent.js";
5
5
  export * from "./agents/mcp-agent.js";
6
6
  export * from "./agents/team-agent.js";
7
+ export * from "./agents/transform-agent.js";
7
8
  export * from "./agents/types.js";
8
9
  export * from "./agents/user-agent.js";
9
10
  export * from "./aigne/index.js";
package/lib/esm/index.js CHANGED
@@ -4,6 +4,7 @@ export * from "./agents/chat-model.js";
4
4
  export * from "./agents/guide-rail-agent.js";
5
5
  export * from "./agents/mcp-agent.js";
6
6
  export * from "./agents/team-agent.js";
7
+ export * from "./agents/transform-agent.js";
7
8
  export * from "./agents/types.js";
8
9
  export * from "./agents/user-agent.js";
9
10
  export * from "./aigne/index.js";
@@ -1,7 +1,7 @@
1
1
  import { type ZodObject, type ZodType, z } from "zod";
2
- import { Agent, type Message } from "../agents/agent.js";
2
+ import { Agent, type FunctionAgentFn } from "../agents/agent.js";
3
3
  export declare function loadAgentFromJsFile(path: string): Promise<Agent<any, any> | {
4
- process: (args_0: Message) => Message;
4
+ process: FunctionAgentFn;
5
5
  name: string;
6
6
  description?: string | undefined;
7
7
  inputSchema?: ZodObject<Record<string, ZodType<any, z.ZodTypeDef, any>>, z.UnknownKeysParam, z.ZodTypeAny, {
@@ -1,22 +1,15 @@
1
1
  import { jsonSchemaToZod } from "@aigne/json-schema-to-zod";
2
+ import camelize from "camelize-ts";
2
3
  import { z } from "zod";
3
4
  import { Agent } from "../agents/agent.js";
4
- import { customCamelize } from "../utils/camelize.js";
5
5
  import { tryOrThrow } from "../utils/type-utils.js";
6
- import { inputOutputSchema } from "./schema.js";
6
+ import { inputOutputSchema, optionalize } from "./schema.js";
7
7
  const agentJsFileSchema = z.object({
8
8
  name: z.string(),
9
- description: z
10
- .string()
11
- .nullish()
12
- .transform((v) => v ?? undefined),
13
- input_schema: inputOutputSchema
14
- .nullish()
15
- .transform((v) => (v ? jsonSchemaToZod(v) : undefined)),
16
- output_schema: inputOutputSchema
17
- .nullish()
18
- .transform((v) => (v ? jsonSchemaToZod(v) : undefined)),
19
- process: z.function(),
9
+ description: optionalize(z.string()),
10
+ inputSchema: optionalize(inputOutputSchema).transform((v) => v ? jsonSchemaToZod(v) : undefined),
11
+ outputSchema: optionalize(inputOutputSchema).transform((v) => v ? jsonSchemaToZod(v) : undefined),
12
+ process: z.custom(),
20
13
  });
21
14
  export async function loadAgentFromJsFile(path) {
22
15
  const { default: agent } = await tryOrThrow(() => import(path), (error) => new Error(`Failed to load agent definition from ${path}: ${error.message}`));
@@ -25,9 +18,9 @@ export async function loadAgentFromJsFile(path) {
25
18
  if (typeof agent !== "function") {
26
19
  throw new Error(`Agent file ${path} must export a default function, but got ${typeof agent}`);
27
20
  }
28
- return tryOrThrow(() => customCamelize(agentJsFileSchema.parse({
21
+ return tryOrThrow(() => agentJsFileSchema.parse(camelize({
29
22
  ...agent,
30
- name: agent.agent_name || agent.name,
23
+ name: agent.agent_name || agent.agentName || agent.name,
31
24
  process: agent,
32
- }), { shallowKeys: ["input_schema", "output_schema"] }), (error) => new Error(`Failed to parse agent from ${path}: ${error.message}`));
25
+ })), (error) => new Error(`Failed to parse agent from ${path}: ${error.message}`));
33
26
  }
@@ -1,48 +1,39 @@
1
- import { type ZodObject, type ZodType, z } from "zod";
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 declare function loadAgentFromYamlFile(path: string): Promise<{
4
+ interface BaseAgentSchema {
5
+ name?: string;
6
+ description?: string;
7
+ inputSchema?: ZodType<Record<string, ZodType>>;
8
+ outputSchema?: ZodType<Record<string, ZodType>>;
9
+ skills?: (string | AgentSchema)[];
10
+ memory?: boolean | {
11
+ provider: string;
12
+ subscribeTopic?: string[];
13
+ };
14
+ }
15
+ interface AIAgentSchema extends BaseAgentSchema {
16
+ type: "ai";
17
+ instructions?: string;
18
+ inputKey?: string;
19
+ outputKey?: string;
20
+ toolChoice?: AIAgentToolChoice;
21
+ }
22
+ interface MCPAgentSchema {
5
23
  type: "mcp";
6
- url?: string | undefined;
7
- command?: string | undefined;
8
- args?: string[] | undefined;
9
- } | {
24
+ url?: string;
25
+ command?: string;
26
+ args?: string[];
27
+ }
28
+ interface TeamAgentSchema extends BaseAgentSchema {
10
29
  type: "team";
11
- name: string;
12
- description?: string | undefined;
13
- skills?: string[] | undefined;
14
- mode?: ProcessMode | undefined;
15
- inputSchema?: ZodObject<Record<string, ZodType<any, z.ZodTypeDef, any>>, z.UnknownKeysParam, z.ZodTypeAny, {
16
- [x: string]: any;
17
- }, {
18
- [x: string]: any;
19
- }> | undefined;
20
- outputSchema?: ZodObject<Record<string, ZodType<any, z.ZodTypeDef, any>>, z.UnknownKeysParam, z.ZodTypeAny, {
21
- [x: string]: any;
22
- }, {
23
- [x: string]: any;
24
- }> | undefined;
25
- } | {
26
- instructions: string | undefined;
27
- type: "ai";
28
- name: string;
29
- description?: string | undefined;
30
- skills?: string[] | undefined;
31
- memory?: true | {
32
- provider: string;
33
- subscribeTopic?: string[] | undefined;
34
- } | undefined;
35
- inputSchema?: ZodObject<Record<string, ZodType<any, z.ZodTypeDef, any>>, z.UnknownKeysParam, z.ZodTypeAny, {
36
- [x: string]: any;
37
- }, {
38
- [x: string]: any;
39
- }> | undefined;
40
- outputSchema?: ZodObject<Record<string, ZodType<any, z.ZodTypeDef, any>>, z.UnknownKeysParam, z.ZodTypeAny, {
41
- [x: string]: any;
42
- }, {
43
- [x: string]: any;
44
- }> | undefined;
45
- inputKey?: string | undefined;
46
- outputKey?: string | undefined;
47
- toolChoice?: AIAgentToolChoice | undefined;
48
- }>;
30
+ mode?: ProcessMode;
31
+ iterateOn?: string;
32
+ }
33
+ interface TransformAgentSchema extends BaseAgentSchema {
34
+ type: "transform";
35
+ jsonata: string;
36
+ }
37
+ type AgentSchema = AIAgentSchema | MCPAgentSchema | TeamAgentSchema | TransformAgentSchema;
38
+ export declare function loadAgentFromYamlFile(path: string): Promise<AgentSchema>;
39
+ export {};
@@ -1,120 +1,72 @@
1
1
  import { jsonSchemaToZod } from "@aigne/json-schema-to-zod";
2
2
  import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
3
+ import camelize from "camelize-ts";
3
4
  import { parse } from "yaml";
4
5
  import { z } from "zod";
5
6
  import { AIAgentToolChoice } from "../agents/ai-agent.js";
6
7
  import { ProcessMode } from "../agents/team-agent.js";
7
- import { customCamelize } from "../utils/camelize.js";
8
8
  import { tryOrThrow } from "../utils/type-utils.js";
9
- import { inputOutputSchema } from "./schema.js";
10
- const agentFileSchema = z.discriminatedUnion("type", [
11
- z.object({
12
- type: z.literal("ai"),
13
- name: z.string(),
14
- description: z
15
- .string()
16
- .nullish()
17
- .transform((v) => v ?? undefined),
18
- instructions: z
19
- .union([
20
- z.string(),
21
- z.object({
22
- url: z.string(),
23
- }),
24
- ])
25
- .nullish()
26
- .transform((v) => v ?? undefined),
27
- input_key: z
28
- .string()
29
- .nullish()
30
- .transform((v) => v ?? undefined),
31
- input_schema: inputOutputSchema
32
- .nullish()
33
- .transform((v) => (v ? jsonSchemaToZod(v) : undefined)),
34
- output_schema: inputOutputSchema
35
- .nullish()
36
- .transform((v) => (v ? jsonSchemaToZod(v) : undefined)),
37
- output_key: z
38
- .string()
39
- .nullish()
40
- .transform((v) => v ?? undefined),
41
- skills: z
42
- .array(z.string())
43
- .nullish()
44
- .transform((v) => v ?? undefined),
45
- tool_choice: z
46
- .nativeEnum(AIAgentToolChoice)
47
- .nullish()
48
- .transform((v) => v ?? undefined),
49
- memory: z
50
- .union([
51
- z.boolean(),
9
+ import { inputOutputSchema, optionalize } from "./schema.js";
10
+ export async function loadAgentFromYamlFile(path) {
11
+ const agentSchema = z.lazy(() => {
12
+ const baseAgentSchema = z.object({
13
+ name: optionalize(z.string()),
14
+ description: optionalize(z.string()),
15
+ inputSchema: optionalize(inputOutputSchema).transform((v) => v ? jsonSchemaToZod(v) : undefined),
16
+ outputSchema: optionalize(inputOutputSchema).transform((v) => v ? jsonSchemaToZod(v) : undefined),
17
+ skills: optionalize(z.array(z.union([z.string(), agentSchema]))),
18
+ memory: optionalize(z.union([
19
+ z.boolean(),
20
+ z.object({
21
+ provider: z.string(),
22
+ subscribeTopic: optionalize(z.array(z.string())),
23
+ }),
24
+ ])),
25
+ });
26
+ return z.discriminatedUnion("type", [
27
+ z
28
+ .object({
29
+ type: z.literal("ai"),
30
+ instructions: optionalize(z.union([
31
+ z.string(),
32
+ z.object({
33
+ url: z.string(),
34
+ }),
35
+ ])).transform((v) => typeof v === "string"
36
+ ? v
37
+ : v && nodejs.fs.readFile(nodejs.path.join(nodejs.path.dirname(path), v.url), "utf8")),
38
+ inputKey: optionalize(z.string()),
39
+ outputKey: optionalize(z.string()),
40
+ toolChoice: optionalize(z.nativeEnum(AIAgentToolChoice)),
41
+ })
42
+ .extend(baseAgentSchema.shape),
52
43
  z.object({
53
- provider: z.string(),
54
- subscribe_topic: z
55
- .array(z.string())
56
- .nullish()
57
- .transform((v) => v ?? undefined),
44
+ type: z.literal("mcp"),
45
+ url: optionalize(z.string()),
46
+ command: optionalize(z.string()),
47
+ args: optionalize(z.array(z.string())),
58
48
  }),
59
- ])
60
- .nullish()
61
- .transform((v) => v || undefined),
62
- }),
63
- z.object({
64
- type: z.literal("mcp"),
65
- url: z
66
- .string()
67
- .nullish()
68
- .transform((v) => v ?? undefined),
69
- command: z
70
- .string()
71
- .nullish()
72
- .transform((v) => v ?? undefined),
73
- args: z
74
- .array(z.string())
75
- .nullish()
76
- .transform((v) => v ?? undefined),
77
- }),
78
- z.object({
79
- type: z.literal("team"),
80
- name: z.string(),
81
- description: z
82
- .string()
83
- .nullish()
84
- .transform((v) => v ?? undefined),
85
- input_schema: inputOutputSchema
86
- .nullish()
87
- .transform((v) => (v ? jsonSchemaToZod(v) : undefined)),
88
- output_schema: inputOutputSchema
89
- .nullish()
90
- .transform((v) => (v ? jsonSchemaToZod(v) : undefined)),
91
- skills: z
92
- .array(z.string())
93
- .nullish()
94
- .transform((v) => v ?? undefined),
95
- mode: z
96
- .nativeEnum(ProcessMode)
97
- .nullish()
98
- .transform((v) => v ?? undefined),
99
- }),
100
- ]);
101
- export async function loadAgentFromYamlFile(path) {
49
+ z
50
+ .object({
51
+ type: z.literal("team"),
52
+ mode: optionalize(z.nativeEnum(ProcessMode)),
53
+ iterateOn: optionalize(z.string()),
54
+ })
55
+ .extend(baseAgentSchema.shape),
56
+ z
57
+ .object({
58
+ type: z.literal("transform"),
59
+ jsonata: z.string(),
60
+ })
61
+ .extend(baseAgentSchema.shape),
62
+ ]);
63
+ });
102
64
  const raw = await tryOrThrow(() => nodejs.fs.readFile(path, "utf8"), (error) => new Error(`Failed to load agent definition from ${path}: ${error.message}`));
103
65
  const json = await tryOrThrow(() => parse(raw), (error) => new Error(`Failed to parse agent definition from ${path}: ${error.message}`));
104
- const agent = await tryOrThrow(async () => customCamelize(await agentFileSchema.parseAsync({
66
+ const agent = await tryOrThrow(async () => await agentSchema.parseAsync(camelize({
105
67
  ...json,
106
68
  type: json.type ?? "ai",
107
69
  skills: json.skills ?? json.tools,
108
- }), {
109
- shallowKeys: ["input_schema", "output_schema"],
110
- }), (error) => new Error(`Failed to validate agent definition from ${path}: ${error.message}`));
111
- if (agent.type === "ai") {
112
- return {
113
- ...agent,
114
- instructions: typeof agent.instructions === "object" && "url" in agent.instructions
115
- ? await nodejs.fs.readFile(nodejs.path.join(nodejs.path.dirname(path), agent.instructions.url), "utf8")
116
- : agent.instructions,
117
- };
118
- }
70
+ })), (error) => new Error(`Failed to validate agent definition from ${path}: ${error.message}`));
119
71
  return agent;
120
72
  }