@aigne/core 1.5.1-1 → 1.5.1-2

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 (70) hide show
  1. package/lib/cjs/agents/mcp-agent.d.ts +34 -7
  2. package/lib/cjs/agents/mcp-agent.js +55 -8
  3. package/lib/cjs/agents/user-agent.d.ts +1 -0
  4. package/lib/cjs/agents/user-agent.js +2 -1
  5. package/lib/cjs/execution-engine/execution-engine.d.ts +9 -2
  6. package/lib/cjs/execution-engine/execution-engine.js +14 -3
  7. package/lib/cjs/loader/ai-agent.d.ts +21 -0
  8. package/lib/cjs/loader/ai-agent.js +40 -0
  9. package/lib/cjs/loader/function-agent.d.ts +21 -0
  10. package/lib/cjs/loader/function-agent.js +67 -0
  11. package/lib/cjs/loader/index.d.ts +25 -0
  12. package/lib/cjs/loader/index.js +93 -0
  13. package/lib/cjs/loader/schema.d.ts +17 -0
  14. package/lib/cjs/loader/schema.js +10 -0
  15. package/lib/cjs/models/claude-chat-model.d.ts +1 -1
  16. package/lib/cjs/models/claude-chat-model.js +3 -2
  17. package/lib/cjs/models/openai-chat-model.d.ts +1 -1
  18. package/lib/cjs/models/openai-chat-model.js +4 -3
  19. package/lib/cjs/models/xai-chat-model.d.ts +2 -0
  20. package/lib/cjs/models/xai-chat-model.js +14 -0
  21. package/lib/cjs/utils/json-schema.js +1 -1
  22. package/lib/cjs/utils/logger.d.ts +3 -1
  23. package/lib/cjs/utils/logger.js +13 -10
  24. package/lib/cjs/utils/mcp-utils.d.ts +4 -5
  25. package/lib/cjs/utils/mcp-utils.js +6 -6
  26. package/lib/cjs/utils/run-chat-loop.js +3 -1
  27. package/lib/cjs/utils/type-utils.d.ts +2 -0
  28. package/lib/cjs/utils/type-utils.js +38 -1
  29. package/lib/dts/agents/mcp-agent.d.ts +34 -7
  30. package/lib/dts/agents/user-agent.d.ts +1 -0
  31. package/lib/dts/execution-engine/execution-engine.d.ts +9 -2
  32. package/lib/dts/loader/ai-agent.d.ts +21 -0
  33. package/lib/dts/loader/function-agent.d.ts +21 -0
  34. package/lib/dts/loader/index.d.ts +25 -0
  35. package/lib/dts/loader/schema.d.ts +17 -0
  36. package/lib/dts/models/claude-chat-model.d.ts +1 -1
  37. package/lib/dts/models/openai-chat-model.d.ts +1 -1
  38. package/lib/dts/models/xai-chat-model.d.ts +2 -0
  39. package/lib/dts/utils/logger.d.ts +3 -1
  40. package/lib/dts/utils/mcp-utils.d.ts +4 -5
  41. package/lib/dts/utils/type-utils.d.ts +2 -0
  42. package/lib/esm/agents/mcp-agent.d.ts +34 -7
  43. package/lib/esm/agents/mcp-agent.js +52 -8
  44. package/lib/esm/agents/user-agent.d.ts +1 -0
  45. package/lib/esm/agents/user-agent.js +1 -0
  46. package/lib/esm/execution-engine/execution-engine.d.ts +9 -2
  47. package/lib/esm/execution-engine/execution-engine.js +15 -4
  48. package/lib/esm/loader/ai-agent.d.ts +21 -0
  49. package/lib/esm/loader/ai-agent.js +37 -0
  50. package/lib/esm/loader/function-agent.d.ts +21 -0
  51. package/lib/esm/loader/function-agent.js +31 -0
  52. package/lib/esm/loader/index.d.ts +25 -0
  53. package/lib/esm/loader/index.js +88 -0
  54. package/lib/esm/loader/schema.d.ts +17 -0
  55. package/lib/esm/loader/schema.js +7 -0
  56. package/lib/esm/models/claude-chat-model.d.ts +1 -1
  57. package/lib/esm/models/claude-chat-model.js +3 -2
  58. package/lib/esm/models/openai-chat-model.d.ts +1 -1
  59. package/lib/esm/models/openai-chat-model.js +4 -3
  60. package/lib/esm/models/xai-chat-model.d.ts +2 -0
  61. package/lib/esm/models/xai-chat-model.js +11 -0
  62. package/lib/esm/utils/json-schema.js +1 -1
  63. package/lib/esm/utils/logger.d.ts +3 -1
  64. package/lib/esm/utils/logger.js +13 -10
  65. package/lib/esm/utils/mcp-utils.d.ts +4 -5
  66. package/lib/esm/utils/mcp-utils.js +6 -6
  67. package/lib/esm/utils/run-chat-loop.js +3 -1
  68. package/lib/esm/utils/type-utils.d.ts +2 -0
  69. package/lib/esm/utils/type-utils.js +36 -1
  70. package/package.json +13 -8
@@ -2,6 +2,7 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
2
  import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
3
3
  import { StdioClientTransport, getDefaultEnvironment, } from "@modelcontextprotocol/sdk/client/stdio.js";
4
4
  import { UriTemplate } from "@modelcontextprotocol/sdk/shared/uriTemplate.js";
5
+ import pRetry from "p-retry";
5
6
  import { z } from "zod";
6
7
  import { logger } from "../utils/logger.js";
7
8
  import { promptFromMCPPrompt, resourceFromMCPResource, toolFromMCPTool, } from "../utils/mcp-utils.js";
@@ -9,6 +10,7 @@ import { checkArguments, createAccessorArray } from "../utils/type-utils.js";
9
10
  import { Agent } from "./agent.js";
10
11
  const MCP_AGENT_CLIENT_NAME = "MCPAgent";
11
12
  const MCP_AGENT_CLIENT_VERSION = "0.0.1";
13
+ const DEFAULT_MAX_RECONNECTS = 10;
12
14
  const debug = logger.base.extend("mcp");
13
15
  function isSSEServerParameters(options) {
14
16
  return "url" in options && typeof options.url === "string";
@@ -29,11 +31,11 @@ export class MCPAgent extends Agent {
29
31
  static from(options) {
30
32
  checkArguments("MCPAgent.from", mcpAgentOptionsSchema, options);
31
33
  if (isSSEServerParameters(options)) {
32
- const transport = new SSEClientTransport(new URL(options.url));
34
+ const transport = () => new SSEClientTransport(new URL(options.url));
33
35
  return MCPAgent.fromTransport(transport, options);
34
36
  }
35
37
  if (isStdioServerParameters(options)) {
36
- const transport = new StdioClientTransport({
38
+ const transport = () => new StdioClientTransport({
37
39
  ...options,
38
40
  env: {
39
41
  ...getDefaultEnvironment(),
@@ -45,23 +47,24 @@ export class MCPAgent extends Agent {
45
47
  }
46
48
  return new MCPAgent(options);
47
49
  }
48
- static async fromTransport(transport, options) {
49
- const client = new Client({
50
+ static async fromTransport(transportCreator, options) {
51
+ const client = new ClientWithReconnect({
50
52
  name: MCP_AGENT_CLIENT_NAME,
51
53
  version: MCP_AGENT_CLIENT_VERSION,
52
- });
54
+ }, undefined, isSSEServerParameters(options) ? { transportCreator, ...options } : undefined);
55
+ const transport = transportCreator();
53
56
  await debug.spinner(client.connect(transport), `Connecting to MCP server: ${getMCPServerString(options)}`);
54
57
  const mcpServer = getMCPServerName(client);
55
58
  const { tools: isToolsAvailable, prompts: isPromptsAvailable, resources: isResourcesAvailable, } = client.getServerCapabilities() ?? {};
56
59
  const tools = isToolsAvailable
57
60
  ? await debug
58
61
  .spinner(client.listTools(), `Listing tools from ${mcpServer}`, ({ tools }) => debug("%O", tools))
59
- .then(({ tools }) => tools.map((tool) => toolFromMCPTool(client, tool)))
62
+ .then(({ tools }) => tools.map((tool) => toolFromMCPTool(tool, { client })))
60
63
  : undefined;
61
64
  const prompts = isPromptsAvailable
62
65
  ? await debug
63
66
  .spinner(client.listPrompts(), `Listing prompts from ${mcpServer}`, ({ prompts }) => debug("%O", prompts))
64
- .then(({ prompts }) => prompts.map((prompt) => promptFromMCPPrompt(client, prompt)))
67
+ .then(({ prompts }) => prompts.map((prompt) => promptFromMCPPrompt(prompt, { client })))
65
68
  : undefined;
66
69
  const resources = isResourcesAvailable
67
70
  ? await debug
@@ -72,7 +75,7 @@ export class MCPAgent extends Agent {
72
75
  client.listResources().catch(() => ({ resources: [] })),
73
76
  client.listResourceTemplates().catch(() => ({ resourceTemplates: [] })),
74
77
  ]), `Listing resources from ${mcpServer}`, ([{ resources }, { resourceTemplates }]) => debug("%O\n%O", resources, resourceTemplates))
75
- .then(([{ resources }, { resourceTemplates }]) => [...resources, ...resourceTemplates].map((resource) => resourceFromMCPResource(client, resource)))
78
+ .then(([{ resources }, { resourceTemplates }]) => [...resources, ...resourceTemplates].map((resource) => resourceFromMCPResource(resource, { client })))
76
79
  : undefined;
77
80
  return new MCPAgent({
78
81
  name: client.getServerVersion()?.name,
@@ -104,6 +107,47 @@ export class MCPAgent extends Agent {
104
107
  await this.client.close();
105
108
  }
106
109
  }
110
+ class ClientWithReconnect extends Client {
111
+ reconnectOptions;
112
+ constructor(info, options, reconnectOptions) {
113
+ super(info, options);
114
+ this.reconnectOptions = reconnectOptions;
115
+ }
116
+ shouldReconnect(error) {
117
+ const { transportCreator, shouldReconnect, maxReconnects } = this.reconnectOptions || {};
118
+ if (!transportCreator || maxReconnects === 0)
119
+ return false;
120
+ if (!shouldReconnect)
121
+ return true; // default to reconnect on all errors
122
+ return shouldReconnect(error);
123
+ }
124
+ async reconnect() {
125
+ const transportCreator = this.reconnectOptions?.transportCreator;
126
+ if (!transportCreator)
127
+ throw new Error("reconnect requires a transportCreator");
128
+ await pRetry(async () => {
129
+ await this.close();
130
+ await this.connect(await transportCreator());
131
+ }, {
132
+ retries: this.reconnectOptions?.maxReconnects ?? DEFAULT_MAX_RECONNECTS,
133
+ shouldRetry: this.shouldReconnect,
134
+ onFailedAttempt: (error) => debug("Reconnect attempt failed: %O", error),
135
+ });
136
+ }
137
+ async request(request, resultSchema, options) {
138
+ try {
139
+ return await super.request(request, resultSchema, options);
140
+ }
141
+ catch (error) {
142
+ if (this.shouldReconnect(error)) {
143
+ debug("Error occurred, reconnecting to MCP server: %O", error);
144
+ await this.reconnect();
145
+ return await super.request(request, resultSchema, options);
146
+ }
147
+ throw error;
148
+ }
149
+ }
150
+ }
107
151
  export class MCPBase extends Agent {
108
152
  constructor(options) {
109
153
  super(options);
@@ -1,3 +1,4 @@
1
+ import { ReadableStream } from "node:stream/web";
1
2
  import type { Context } from "../execution-engine/context.js";
2
3
  import type { MessagePayload, MessageQueueListener, Unsubscribe } from "../execution-engine/message-queue.js";
3
4
  import { type PromiseOrValue } from "../utils/type-utils.js";
@@ -1,3 +1,4 @@
1
+ import { ReadableStream } from "node:stream/web";
1
2
  import { orArrayToArray } from "../utils/type-utils.js";
2
3
  import { Agent } from "./agent.js";
3
4
  export class UserAgent extends Agent {
@@ -11,11 +11,18 @@ export interface ExecutionEngineOptions {
11
11
  limits?: ContextLimits;
12
12
  }
13
13
  export declare class ExecutionEngine extends EventEmitter {
14
+ static load({ path, ...options }: {
15
+ path: string;
16
+ } & ExecutionEngineOptions): Promise<ExecutionEngine>;
14
17
  constructor(options?: ExecutionEngineOptions);
15
18
  readonly messageQueue: MessageQueue;
16
19
  model?: ChatModel;
17
- tools: Agent[];
18
- private agents;
20
+ readonly tools: Agent<Message, Message>[] & {
21
+ [key: string]: Agent<Message, Message>;
22
+ };
23
+ readonly agents: Agent<Message, Message>[] & {
24
+ [key: string]: Agent<Message, Message>;
25
+ };
19
26
  limits?: ContextLimits;
20
27
  addAgent(...agents: Agent[]): void;
21
28
  newContext(): ExecutionContext;
@@ -1,26 +1,37 @@
1
1
  import EventEmitter from "node:events";
2
2
  import { z } from "zod";
3
3
  import { Agent } from "../agents/agent.js";
4
+ import { load } from "../loader/index.js";
4
5
  import { ChatModel } from "../models/chat-model.js";
5
- import { checkArguments } from "../utils/type-utils.js";
6
+ import { checkArguments, createAccessorArray } from "../utils/type-utils.js";
6
7
  import { ExecutionContext } from "./context.js";
7
8
  import { MessageQueue, } from "./message-queue.js";
8
9
  export class ExecutionEngine extends EventEmitter {
10
+ static async load({ path, ...options }) {
11
+ const { model, agents, tools } = await load({ path });
12
+ return new ExecutionEngine({
13
+ model,
14
+ ...options,
15
+ agents: agents.concat(options.agents ?? []),
16
+ tools: tools.concat(options.tools ?? []),
17
+ });
18
+ }
9
19
  constructor(options) {
10
20
  if (options)
11
21
  checkArguments("ExecutionEngine", executionEngineOptionsSchema, options);
12
22
  super();
13
23
  this.model = options?.model;
14
- this.tools = options?.tools ?? [];
15
24
  this.limits = options?.limits;
25
+ if (options?.tools?.length)
26
+ this.tools.push(...options.tools);
16
27
  if (options?.agents?.length)
17
28
  this.addAgent(...options.agents);
18
29
  this.initProcessExitHandler();
19
30
  }
20
31
  messageQueue = new MessageQueue();
21
32
  model;
22
- tools;
23
- agents = [];
33
+ tools = createAccessorArray([], (arr, name) => arr.find((i) => i.name === name));
34
+ agents = createAccessorArray([], (arr, name) => arr.find((i) => i.name === name));
24
35
  limits;
25
36
  addAgent(...agents) {
26
37
  checkArguments("ExecutionEngine.addAgent", executionEngineAddAgentArgsSchema, agents);
@@ -0,0 +1,21 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { type ZodObject, type ZodType, z } from "zod";
3
+ export declare function loadAgentFromYamlFile(path: string, { readFile: _readFile }?: {
4
+ readFile?: typeof readFile;
5
+ }): Promise<{
6
+ name: string;
7
+ description?: string | undefined;
8
+ tools?: string[] | undefined;
9
+ instructions?: string | undefined;
10
+ input_schema?: ZodObject<Record<string, ZodType<any, z.ZodTypeDef, any>>, z.UnknownKeysParam, z.ZodTypeAny, {
11
+ [x: string]: any;
12
+ }, {
13
+ [x: string]: any;
14
+ }> | undefined;
15
+ output_schema?: ZodObject<Record<string, ZodType<any, z.ZodTypeDef, any>>, z.UnknownKeysParam, z.ZodTypeAny, {
16
+ [x: string]: any;
17
+ }, {
18
+ [x: string]: any;
19
+ }> | undefined;
20
+ output_key?: string | undefined;
21
+ }>;
@@ -0,0 +1,37 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { jsonSchemaToZod } from "@aigne/json-schema-to-zod";
3
+ import { parse } from "yaml";
4
+ import { z } from "zod";
5
+ import { tryOrThrow } from "../utils/type-utils.js";
6
+ import { inputOutputSchema } from "./schema.js";
7
+ const agentFileSchema = z.object({
8
+ name: z.string(),
9
+ description: z
10
+ .string()
11
+ .nullish()
12
+ .transform((v) => v ?? undefined),
13
+ instructions: z
14
+ .string()
15
+ .nullish()
16
+ .transform((v) => v ?? undefined),
17
+ input_schema: inputOutputSchema
18
+ .nullish()
19
+ .transform((v) => (v ? jsonSchemaToZod(v) : undefined)),
20
+ output_schema: inputOutputSchema
21
+ .nullish()
22
+ .transform((v) => (v ? jsonSchemaToZod(v) : undefined)),
23
+ output_key: z
24
+ .string()
25
+ .nullish()
26
+ .transform((v) => v ?? undefined),
27
+ tools: z
28
+ .array(z.string())
29
+ .nullish()
30
+ .transform((v) => v ?? undefined),
31
+ });
32
+ export async function loadAgentFromYamlFile(path, { readFile: _readFile = readFile } = {}) {
33
+ const raw = await tryOrThrow(() => _readFile(path, "utf8"), (error) => new Error(`Failed to load agent definition from ${path}: ${error.message}`));
34
+ const json = await tryOrThrow(() => parse(raw), (error) => new Error(`Failed to parse agent definition from ${path}: ${error.message}`));
35
+ const agent = tryOrThrow(() => agentFileSchema.parse(json), (error) => new Error(`Failed to validate agent definition from ${path}: ${error.message}`));
36
+ return agent;
37
+ }
@@ -0,0 +1,21 @@
1
+ import { type ZodObject, type ZodType, z } from "zod";
2
+ import type { Message } from "../agents/agent.js";
3
+ export declare function loadAgentFromJsFile(path: string, { import: _import }?: {
4
+ import?: (path: string) => Promise<{
5
+ default: unknown;
6
+ }>;
7
+ }): Promise<{
8
+ name: string;
9
+ fn: (args_0: Message) => Message;
10
+ description?: string | undefined;
11
+ input_schema?: ZodObject<Record<string, ZodType<any, z.ZodTypeDef, any>>, z.UnknownKeysParam, z.ZodTypeAny, {
12
+ [x: string]: any;
13
+ }, {
14
+ [x: string]: any;
15
+ }> | undefined;
16
+ output_schema?: ZodObject<Record<string, ZodType<any, z.ZodTypeDef, any>>, z.UnknownKeysParam, z.ZodTypeAny, {
17
+ [x: string]: any;
18
+ }, {
19
+ [x: string]: any;
20
+ }> | undefined;
21
+ }>;
@@ -0,0 +1,31 @@
1
+ import { jsonSchemaToZod } from "@aigne/json-schema-to-zod";
2
+ import { z } from "zod";
3
+ import { tryOrThrow } from "../utils/type-utils.js";
4
+ import { inputOutputSchema } from "./schema.js";
5
+ const agentJsFileSchema = z.object({
6
+ name: z.string(),
7
+ description: z
8
+ .string()
9
+ .nullish()
10
+ .transform((v) => v ?? undefined),
11
+ input_schema: inputOutputSchema
12
+ .nullish()
13
+ .transform((v) => (v ? jsonSchemaToZod(v) : undefined)),
14
+ output_schema: inputOutputSchema
15
+ .nullish()
16
+ .transform((v) => (v ? jsonSchemaToZod(v) : undefined)),
17
+ fn: z.function(),
18
+ });
19
+ export async function loadAgentFromJsFile(path, { import: _import } = {}) {
20
+ const { default: agent } = await tryOrThrow(() => (_import ? _import(path) : import(path)), (error) => new Error(`Failed to load agent definition from ${path}: ${error.message}`));
21
+ if (typeof agent !== "function") {
22
+ throw new Error(`Agent file ${path} must export a default function, but got ${typeof agent}`);
23
+ }
24
+ return tryOrThrow(() => agentJsFileSchema.parse({
25
+ name: agent.name,
26
+ description: agent.description,
27
+ input_schema: agent.input_schema,
28
+ output_schema: agent.output_schema,
29
+ fn: agent,
30
+ }), (error) => new Error(`Failed to parse agent from ${path}: ${error.message}`));
31
+ }
@@ -0,0 +1,25 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { type Agent } from "../agents/agent.js";
3
+ import type { ChatModel } from "../models/chat-model.js";
4
+ export interface LoadOptions {
5
+ path: string;
6
+ }
7
+ export declare function load(options: LoadOptions): Promise<{
8
+ model: ChatModel | undefined;
9
+ agents: Agent<import("../agents/agent.js").Message, import("../agents/agent.js").Message>[];
10
+ tools: Agent<import("../agents/agent.js").Message, import("../agents/agent.js").Message>[];
11
+ }>;
12
+ export declare function loadAgent(path: string): Promise<Agent>;
13
+ export declare function loadAIGNEFile(path: string, { readFile: _readFile }?: {
14
+ readFile?: typeof readFile;
15
+ }): Promise<{
16
+ tools?: string[] | null | undefined;
17
+ chat_model?: {
18
+ name?: string | null | undefined;
19
+ temperature?: number | null | undefined;
20
+ top_p?: number | null | undefined;
21
+ frequent_penalty?: number | null | undefined;
22
+ presence_penalty?: number | null | undefined;
23
+ } | null | undefined;
24
+ agents?: string[] | null | undefined;
25
+ }>;
@@ -0,0 +1,88 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { dirname, extname, join } from "node:path";
3
+ import { parse } from "yaml";
4
+ import { z } from "zod";
5
+ import { FunctionAgent } from "../agents/agent.js";
6
+ import { AIAgent } from "../agents/ai-agent.js";
7
+ import { OpenAIChatModel } from "../models/openai-chat-model.js";
8
+ import { tryOrThrow } from "../utils/type-utils.js";
9
+ import { loadAgentFromYamlFile } from "./ai-agent.js";
10
+ import { loadAgentFromJsFile } from "./function-agent.js";
11
+ const AIGNE_FILE_NAME = "aigne.yaml";
12
+ export async function load(options) {
13
+ const { path } = options;
14
+ const aigneFilePath = path.endsWith(AIGNE_FILE_NAME) ? path : join(path, AIGNE_FILE_NAME);
15
+ const rootDir = dirname(aigneFilePath);
16
+ const aigne = await loadAIGNEFile(aigneFilePath);
17
+ const agents = await Promise.all((aigne.agents ?? []).map((filename) => loadAgent(join(rootDir, filename))));
18
+ const tools = await Promise.all((aigne.tools ?? []).map((filename) => loadAgent(join(rootDir, filename))));
19
+ return {
20
+ model: await loadModel(aigne.chat_model),
21
+ agents,
22
+ tools,
23
+ };
24
+ }
25
+ export async function loadAgent(path) {
26
+ if (extname(path) === ".js") {
27
+ const agent = await loadAgentFromJsFile(path);
28
+ return FunctionAgent.from({
29
+ name: agent.name,
30
+ description: agent.description,
31
+ inputSchema: agent.input_schema,
32
+ outputSchema: agent.output_schema,
33
+ fn: agent.fn,
34
+ });
35
+ }
36
+ if (extname(path) === ".yaml" || extname(path) === ".yml") {
37
+ const agent = await loadAgentFromYamlFile(path);
38
+ return AIAgent.from({
39
+ name: agent.name,
40
+ description: agent.description,
41
+ instructions: agent.instructions,
42
+ inputSchema: agent.input_schema,
43
+ outputSchema: agent.output_schema,
44
+ outputKey: agent.output_key,
45
+ tools: await Promise.all((agent.tools ?? []).map((filename) => loadAgent(join(dirname(path), filename)))),
46
+ });
47
+ }
48
+ throw new Error(`Unsupported agent file type: ${path}`);
49
+ }
50
+ async function loadModel(model) {
51
+ if (!model?.name)
52
+ return undefined;
53
+ const params = {
54
+ model: model.name,
55
+ temperature: model.temperature ?? undefined,
56
+ topP: model.top_p ?? undefined,
57
+ frequencyPenalty: model.frequent_penalty ?? undefined,
58
+ presencePenalty: model.presence_penalty ?? undefined,
59
+ };
60
+ // TODO: add support for other models such as AutoChatModel, ClaudeChatModel, etc.
61
+ if (/^o1|gpt-/.test(model.name)) {
62
+ return new OpenAIChatModel(params);
63
+ }
64
+ throw new Error(`Unsupported model: ${model.name}`);
65
+ }
66
+ const aigneFileSchema = z.object({
67
+ chat_model: z
68
+ .union([
69
+ z.string(),
70
+ z.object({
71
+ name: z.string().nullish(),
72
+ temperature: z.number().min(0).max(2).nullish(),
73
+ top_p: z.number().min(0).nullish(),
74
+ frequent_penalty: z.number().min(-2).max(2).nullish(),
75
+ presence_penalty: z.number().min(-2).max(2).nullish(),
76
+ }),
77
+ ])
78
+ .nullish()
79
+ .transform((v) => (typeof v === "string" ? { name: v } : v)),
80
+ agents: z.array(z.string()).nullish(),
81
+ tools: z.array(z.string()).nullish(),
82
+ });
83
+ export async function loadAIGNEFile(path, { readFile: _readFile = readFile } = {}) {
84
+ const raw = await tryOrThrow(() => _readFile(path, "utf8"), (error) => new Error(`Failed to load aigne.yaml from ${path}: ${error.message}`));
85
+ const json = await tryOrThrow(() => parse(raw), (error) => new Error(`Failed to parse aigne.yaml from ${path}: ${error.message}`));
86
+ const agent = tryOrThrow(() => aigneFileSchema.parse(json), (error) => new Error(`Failed to validate aigne.yaml from ${path}: ${error.message}`));
87
+ return agent;
88
+ }
@@ -0,0 +1,17 @@
1
+ import { z } from "zod";
2
+ export declare const inputOutputSchema: z.ZodObject<{
3
+ type: z.ZodLiteral<"object">;
4
+ properties: z.ZodRecord<z.ZodString, z.ZodAny>;
5
+ required: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
6
+ additionalProperties: z.ZodOptional<z.ZodBoolean>;
7
+ }, "strip", z.ZodTypeAny, {
8
+ type: "object";
9
+ properties: Record<string, any>;
10
+ required?: string[] | undefined;
11
+ additionalProperties?: boolean | undefined;
12
+ }, {
13
+ type: "object";
14
+ properties: Record<string, any>;
15
+ required?: string[] | undefined;
16
+ additionalProperties?: boolean | undefined;
17
+ }>;
@@ -0,0 +1,7 @@
1
+ import { z } from "zod";
2
+ export const inputOutputSchema = z.object({
3
+ type: z.literal("object"),
4
+ properties: z.record(z.any()),
5
+ required: z.array(z.string()).optional(),
6
+ additionalProperties: z.boolean().optional(),
7
+ });
@@ -57,7 +57,7 @@ export declare const claudeChatModelOptionsSchema: z.ZodObject<{
57
57
  export declare class ClaudeChatModel extends ChatModel {
58
58
  options?: ClaudeChatModelOptions | undefined;
59
59
  constructor(options?: ClaudeChatModelOptions | undefined);
60
- private _client?;
60
+ protected _client?: Anthropic;
61
61
  get client(): Anthropic;
62
62
  get modelOptions(): ChatModelOptions | undefined;
63
63
  process(input: ChatModelInput): Promise<ChatModelOutput>;
@@ -30,9 +30,10 @@ export class ClaudeChatModel extends ChatModel {
30
30
  }
31
31
  _client;
32
32
  get client() {
33
- if (!this.options?.apiKey)
33
+ const apiKey = this.options?.apiKey || process.env.CLAUDE_API_KEY;
34
+ if (!apiKey)
34
35
  throw new Error("Api Key is required for ClaudeChatModel");
35
- this._client ??= new Anthropic({ apiKey: this.options.apiKey });
36
+ this._client ??= new Anthropic({ apiKey });
36
37
  return this._client;
37
38
  }
38
39
  get modelOptions() {
@@ -61,7 +61,7 @@ export declare const openAIChatModelOptionsSchema: z.ZodObject<{
61
61
  export declare class OpenAIChatModel extends ChatModel {
62
62
  options?: OpenAIChatModelOptions | undefined;
63
63
  constructor(options?: OpenAIChatModelOptions | undefined);
64
- private _client?;
64
+ protected _client?: OpenAI;
65
65
  get client(): OpenAI;
66
66
  get modelOptions(): ChatModelOptions | undefined;
67
67
  process(input: ChatModelInput): Promise<ChatModelOutput>;
@@ -30,11 +30,12 @@ export class OpenAIChatModel extends ChatModel {
30
30
  }
31
31
  _client;
32
32
  get client() {
33
- if (!this.options?.apiKey)
33
+ const apiKey = this.options?.apiKey || process.env.OPENAI_API_KEY;
34
+ if (!apiKey)
34
35
  throw new Error("Api Key is required for OpenAIChatModel");
35
36
  this._client ??= new OpenAI({
36
- baseURL: this.options.baseURL,
37
- apiKey: this.options.apiKey,
37
+ baseURL: this.options?.baseURL,
38
+ apiKey,
38
39
  });
39
40
  return this._client;
40
41
  }
@@ -1,3 +1,4 @@
1
+ import OpenAI from "openai";
1
2
  import type { ChatModelOptions } from "./chat-model.js";
2
3
  import { OpenAIChatModel } from "./openai-chat-model.js";
3
4
  export interface XAIChatModelOptions {
@@ -8,4 +9,5 @@ export interface XAIChatModelOptions {
8
9
  }
9
10
  export declare class XAIChatModel extends OpenAIChatModel {
10
11
  constructor(options?: XAIChatModelOptions);
12
+ get client(): OpenAI;
11
13
  }
@@ -1,3 +1,4 @@
1
+ import OpenAI from "openai";
1
2
  import { OpenAIChatModel } from "./openai-chat-model.js";
2
3
  const XAI_DEFAULT_CHAT_MODEL = "grok-2-latest";
3
4
  const XAI_BASE_URL = "https://api.x.ai/v1";
@@ -9,4 +10,14 @@ export class XAIChatModel extends OpenAIChatModel {
9
10
  baseURL: options?.baseURL || XAI_BASE_URL,
10
11
  });
11
12
  }
13
+ get client() {
14
+ const apiKey = this.options?.apiKey || process.env.XAI_API_KEY;
15
+ if (!apiKey)
16
+ throw new Error("Api Key is required for XAIChatModel");
17
+ this._client ??= new OpenAI({
18
+ baseURL: this.options?.baseURL,
19
+ apiKey,
20
+ });
21
+ return this._client;
22
+ }
12
23
  }
@@ -7,7 +7,7 @@ function setAdditionPropertiesDeep(schema, additionalProperties) {
7
7
  if (Array.isArray(schema)) {
8
8
  return schema.map((s) => setAdditionPropertiesDeep(s, additionalProperties));
9
9
  }
10
- if (schema && typeof schema === "object" && !Array.isArray(schema)) {
10
+ if (schema !== null && typeof schema === "object" && !Array.isArray(schema)) {
11
11
  return Object.entries(schema).reduce((acc, [key, value]) => {
12
12
  acc[key] = setAdditionPropertiesDeep(value, additionalProperties);
13
13
  if (acc.type === "object") {
@@ -1,4 +1,5 @@
1
1
  import debug, { type Debugger } from "debug";
2
+ import type { Ora } from "ora";
2
3
  interface DebugWithSpinner extends Debugger {
3
4
  spinner<T>(promise: Promise<T>, message?: string, callback?: (result: T) => void, options?: {
4
5
  disabled?: boolean;
@@ -10,9 +11,10 @@ export declare const logger: debug.Debug & {
10
11
  debug: debug.Debug;
11
12
  default: debug.Debug;
12
13
  } & {
13
- globalSpinner: import("@aigne/ora").Ora;
14
+ globalSpinner: Ora | undefined;
14
15
  base: DebugWithSpinner;
15
16
  debug: DebugWithSpinner;
16
17
  spinner: typeof spinner;
18
+ setSpinner: (spinner: Ora) => void;
17
19
  };
18
20
  export {};
@@ -1,12 +1,11 @@
1
- import ora from "@aigne/ora";
2
1
  import debug from "debug";
3
2
  debug.log = (...args) => {
4
- const { isSpinning } = globalSpinner;
3
+ const { isSpinning } = globalSpinner ?? {};
5
4
  if (isSpinning)
6
- globalSpinner.stop();
5
+ globalSpinner?.stop();
7
6
  console.log(...args);
8
7
  if (isSpinning)
9
- globalSpinner.start();
8
+ globalSpinner?.start();
10
9
  };
11
10
  function createDebugger(namespace) {
12
11
  const i = debug(namespace);
@@ -26,12 +25,12 @@ function createDebugger(namespace) {
26
25
  overrideExtend(i);
27
26
  return i;
28
27
  }
29
- const globalSpinner = ora();
28
+ let globalSpinner;
30
29
  const globalSpinnerTasks = [];
31
30
  async function spinner(promise, message, callback) {
32
31
  const task = { promise, message, callback };
33
32
  globalSpinnerTasks.push(task);
34
- globalSpinner.start(message || " ");
33
+ globalSpinner?.start(message || " ");
35
34
  await promise
36
35
  .then((result) => {
37
36
  task.result = result;
@@ -48,18 +47,18 @@ async function spinner(promise, message, callback) {
48
47
  break;
49
48
  // Recover spinner state for last running task
50
49
  if (!task.status) {
51
- globalSpinner.start(task.message || " ");
50
+ globalSpinner?.start(task.message || " ");
52
51
  break;
53
52
  }
54
53
  globalSpinnerTasks.pop();
55
54
  if (task.message) {
56
55
  if (task.status === "fail")
57
- globalSpinner.fail(task.message);
56
+ globalSpinner?.fail(task.message);
58
57
  else
59
- globalSpinner.succeed(task.message);
58
+ globalSpinner?.succeed(task.message);
60
59
  }
61
60
  else {
62
- globalSpinner.stop();
61
+ globalSpinner?.stop();
63
62
  }
64
63
  // NOTE: This is a workaround to make sure the spinner stops spinning before the next tick
65
64
  await new Promise((resolve) => setTimeout(resolve, 10));
@@ -74,4 +73,8 @@ export const logger = Object.assign(debug, {
74
73
  base,
75
74
  debug: base.extend("core"),
76
75
  spinner,
76
+ setSpinner: (spinner) => {
77
+ globalSpinner = spinner;
78
+ logger.globalSpinner = spinner;
79
+ },
77
80
  });
@@ -1,6 +1,5 @@
1
- import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
1
  import { type ListPromptsResult, type ListResourceTemplatesResult, type ListResourcesResult, type ListToolsResult } from "@modelcontextprotocol/sdk/types.js";
3
- import { MCPPrompt, MCPResource, MCPTool } from "../agents/mcp-agent.js";
4
- export declare function toolFromMCPTool(client: Client, tool: ListToolsResult["tools"][number]): MCPTool;
5
- export declare function promptFromMCPPrompt(client: Client, prompt: ListPromptsResult["prompts"][number]): MCPPrompt;
6
- export declare function resourceFromMCPResource(client: Client, resource: ListResourcesResult["resources"][number] | ListResourceTemplatesResult["resourceTemplates"][number]): MCPResource;
2
+ import { type MCPBaseOptions, MCPPrompt, MCPResource, MCPTool } from "../agents/mcp-agent.js";
3
+ export declare function toolFromMCPTool(tool: ListToolsResult["tools"][number], options: MCPBaseOptions): MCPTool;
4
+ export declare function promptFromMCPPrompt(prompt: ListPromptsResult["prompts"][number], options: MCPBaseOptions): MCPPrompt;
5
+ export declare function resourceFromMCPResource(resource: ListResourcesResult["resources"][number] | ListResourceTemplatesResult["resourceTemplates"][number], options: MCPBaseOptions): MCPResource;