@aigne/core 1.6.0 → 1.8.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 +27 -0
- package/lib/cjs/agents/agent.d.ts +5 -2
- package/lib/cjs/agents/agent.js +42 -24
- package/lib/cjs/agents/ai-agent.d.ts +8 -8
- package/lib/cjs/agents/ai-agent.js +5 -2
- package/lib/cjs/agents/mcp-agent.d.ts +12 -2
- package/lib/cjs/agents/mcp-agent.js +45 -27
- package/lib/cjs/agents/user-agent.d.ts +9 -9
- package/lib/cjs/agents/user-agent.js +26 -16
- package/lib/cjs/execution-engine/context.d.ts +84 -46
- package/lib/cjs/execution-engine/context.js +136 -98
- package/lib/cjs/execution-engine/execution-engine.d.ts +16 -47
- package/lib/cjs/execution-engine/execution-engine.js +14 -41
- package/lib/cjs/execution-engine/message-queue.d.ts +3 -3
- package/lib/cjs/execution-engine/message-queue.js +32 -2
- package/lib/cjs/execution-engine/usage.d.ts +11 -0
- package/lib/cjs/execution-engine/usage.js +10 -0
- package/lib/cjs/loader/agent-js.js +1 -1
- package/lib/cjs/loader/agent-yaml.d.ts +1 -0
- package/lib/cjs/loader/agent-yaml.js +4 -0
- package/lib/cjs/loader/index.d.ts +72 -1
- package/lib/cjs/loader/index.js +13 -13
- package/lib/cjs/models/chat-model.d.ts +3 -2
- package/lib/cjs/models/chat-model.js +6 -5
- package/lib/cjs/models/claude-chat-model.js +11 -8
- package/lib/cjs/models/openai-chat-model.js +5 -2
- package/lib/cjs/prompt/prompt-builder.d.ts +1 -1
- package/lib/cjs/prompt/prompt-builder.js +3 -1
- package/lib/cjs/utils/json-schema.js +2 -2
- package/lib/cjs/utils/logger.d.ts +3 -15
- package/lib/cjs/utils/logger.js +3 -77
- package/lib/cjs/utils/mcp-utils.js +1 -5
- package/lib/cjs/utils/model-utils.js +2 -2
- package/lib/cjs/utils/type-utils.d.ts +1 -0
- package/lib/cjs/utils/typed-event-emtter.d.ts +10 -0
- package/lib/cjs/utils/typed-event-emtter.js +2 -0
- package/lib/dts/agents/agent.d.ts +5 -2
- package/lib/dts/agents/ai-agent.d.ts +8 -8
- package/lib/dts/agents/mcp-agent.d.ts +12 -2
- package/lib/dts/agents/user-agent.d.ts +9 -9
- package/lib/dts/execution-engine/context.d.ts +84 -46
- package/lib/dts/execution-engine/execution-engine.d.ts +16 -47
- package/lib/dts/execution-engine/message-queue.d.ts +3 -3
- package/lib/dts/execution-engine/usage.d.ts +11 -0
- package/lib/dts/loader/agent-yaml.d.ts +1 -0
- package/lib/dts/loader/index.d.ts +72 -1
- package/lib/dts/models/chat-model.d.ts +3 -2
- package/lib/dts/prompt/prompt-builder.d.ts +1 -1
- package/lib/dts/utils/logger.d.ts +3 -15
- package/lib/dts/utils/type-utils.d.ts +1 -0
- package/lib/dts/utils/typed-event-emtter.d.ts +10 -0
- package/lib/esm/agents/agent.d.ts +5 -2
- package/lib/esm/agents/agent.js +42 -24
- package/lib/esm/agents/ai-agent.d.ts +8 -8
- package/lib/esm/agents/ai-agent.js +5 -2
- package/lib/esm/agents/mcp-agent.d.ts +12 -2
- package/lib/esm/agents/mcp-agent.js +46 -28
- package/lib/esm/agents/user-agent.d.ts +9 -9
- package/lib/esm/agents/user-agent.js +26 -16
- package/lib/esm/execution-engine/context.d.ts +84 -46
- package/lib/esm/execution-engine/context.js +135 -98
- package/lib/esm/execution-engine/execution-engine.d.ts +16 -47
- package/lib/esm/execution-engine/execution-engine.js +15 -39
- package/lib/esm/execution-engine/message-queue.d.ts +3 -3
- package/lib/esm/execution-engine/message-queue.js +33 -3
- package/lib/esm/execution-engine/usage.d.ts +11 -0
- package/lib/esm/execution-engine/usage.js +7 -0
- package/lib/esm/loader/agent-js.js +1 -1
- package/lib/esm/loader/agent-yaml.d.ts +1 -0
- package/lib/esm/loader/agent-yaml.js +4 -0
- package/lib/esm/loader/index.d.ts +72 -1
- package/lib/esm/loader/index.js +13 -14
- package/lib/esm/models/chat-model.d.ts +3 -2
- package/lib/esm/models/chat-model.js +6 -5
- package/lib/esm/models/claude-chat-model.js +11 -8
- package/lib/esm/models/openai-chat-model.js +5 -2
- package/lib/esm/prompt/prompt-builder.d.ts +1 -1
- package/lib/esm/prompt/prompt-builder.js +3 -1
- package/lib/esm/utils/json-schema.js +2 -2
- package/lib/esm/utils/logger.d.ts +3 -15
- package/lib/esm/utils/logger.js +3 -77
- package/lib/esm/utils/mcp-utils.js +1 -5
- package/lib/esm/utils/model-utils.js +2 -2
- package/lib/esm/utils/type-utils.d.ts +1 -0
- package/lib/esm/utils/typed-event-emtter.d.ts +10 -0
- package/lib/esm/utils/typed-event-emtter.js +1 -0
- package/package.json +11 -9
- package/lib/cjs/utils/run-chat-loop.d.ts +0 -11
- package/lib/cjs/utils/run-chat-loop.js +0 -82
- package/lib/dts/utils/run-chat-loop.d.ts +0 -11
- package/lib/esm/utils/run-chat-loop.d.ts +0 -11
- package/lib/esm/utils/run-chat-loop.js +0 -76
package/lib/esm/agents/agent.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { inspect } from "node:util";
|
|
1
2
|
import { ZodObject, z } from "zod";
|
|
2
3
|
import { createMessage } from "../prompt/prompt-builder.js";
|
|
3
4
|
import { logger } from "../utils/logger.js";
|
|
@@ -19,7 +20,7 @@ export class Agent {
|
|
|
19
20
|
this.publishTopic = options.publishTopic;
|
|
20
21
|
if (options.tools?.length)
|
|
21
22
|
this.tools.push(...options.tools.map(functionToAgent));
|
|
22
|
-
this.
|
|
23
|
+
this.disableEvents = options.disableEvents;
|
|
23
24
|
if (options.memory) {
|
|
24
25
|
this.memory =
|
|
25
26
|
options.memory instanceof AgentMemory
|
|
@@ -56,7 +57,7 @@ export class Agent {
|
|
|
56
57
|
subscribeTopic;
|
|
57
58
|
publishTopic;
|
|
58
59
|
tools = createAccessorArray([], (arr, name) => arr.find((t) => t.name === name));
|
|
59
|
-
|
|
60
|
+
disableEvents;
|
|
60
61
|
/**
|
|
61
62
|
* Attach agent to context:
|
|
62
63
|
* - subscribe to topic and call process method when message received
|
|
@@ -71,7 +72,7 @@ export class Agent {
|
|
|
71
72
|
await context.call(this, message);
|
|
72
73
|
}
|
|
73
74
|
catch (error) {
|
|
74
|
-
context.emit("
|
|
75
|
+
context.emit("agentFailed", { agent: this, error });
|
|
75
76
|
}
|
|
76
77
|
});
|
|
77
78
|
}
|
|
@@ -95,31 +96,45 @@ export class Agent {
|
|
|
95
96
|
}
|
|
96
97
|
async call(input, context) {
|
|
97
98
|
const ctx = context ?? (await this.newDefaultContext());
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
99
|
+
const message = typeof input === "string" ? createMessage(input) : input;
|
|
100
|
+
logger.core("Call agent %s started with input: %O", this.name, input);
|
|
101
|
+
if (!this.disableEvents)
|
|
102
|
+
ctx.emit("agentStarted", { agent: this, input: message });
|
|
103
|
+
try {
|
|
104
|
+
const parsedInput = this.inputSchema.parse(message);
|
|
105
|
+
this.preprocess(parsedInput, ctx);
|
|
106
|
+
this.checkContextStatus(ctx);
|
|
107
|
+
const output = await this.process(parsedInput, ctx)
|
|
108
|
+
.then((output) => {
|
|
109
|
+
const parsedOutput = this.outputSchema.parse(output);
|
|
110
|
+
return this.includeInputInOutput ? { ...parsedInput, ...parsedOutput } : parsedOutput;
|
|
111
|
+
})
|
|
112
|
+
.then((output) => {
|
|
113
|
+
this.postprocess(parsedInput, output, ctx);
|
|
114
|
+
return output;
|
|
115
|
+
});
|
|
116
|
+
logger.core("Call agent %s succeed with output: %O", this.name, input);
|
|
117
|
+
if (!this.disableEvents)
|
|
118
|
+
ctx.emit("agentSucceed", { agent: this, output });
|
|
110
119
|
return output;
|
|
111
|
-
}
|
|
112
|
-
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
logger.core("Call agent %s failed with error: %O", this.name, error);
|
|
123
|
+
if (!this.disableEvents)
|
|
124
|
+
ctx.emit("agentFailed", { agent: this, error });
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
checkUsageAgentCalls(context) {
|
|
129
|
+
const { limits, usage } = context;
|
|
130
|
+
if (limits?.maxAgentCalls && usage.agentCalls >= limits.maxAgentCalls) {
|
|
131
|
+
throw new Error(`Exceeded max agent calls ${usage.agentCalls}/${limits.maxAgentCalls}`);
|
|
132
|
+
}
|
|
133
|
+
usage.agentCalls++;
|
|
113
134
|
}
|
|
114
135
|
preprocess(_, context) {
|
|
115
136
|
this.checkContextStatus(context);
|
|
116
|
-
|
|
117
|
-
const { limits, usage } = context;
|
|
118
|
-
if (limits?.maxAgentCalls && usage.agentCalls >= limits.maxAgentCalls) {
|
|
119
|
-
throw new Error(`Exceeded max agent calls ${usage.agentCalls}/${limits.maxAgentCalls}`);
|
|
120
|
-
}
|
|
121
|
-
usage.agentCalls++;
|
|
122
|
-
}
|
|
137
|
+
this.checkUsageAgentCalls(context);
|
|
123
138
|
}
|
|
124
139
|
postprocess(input, output, context) {
|
|
125
140
|
this.checkContextStatus(context);
|
|
@@ -133,6 +148,9 @@ export class Agent {
|
|
|
133
148
|
async shutdown() {
|
|
134
149
|
this.memory?.detach();
|
|
135
150
|
}
|
|
151
|
+
[inspect.custom]() {
|
|
152
|
+
return this.name;
|
|
153
|
+
}
|
|
136
154
|
}
|
|
137
155
|
function checkAgentInputOutputSchema(schema) {
|
|
138
156
|
if (!(schema instanceof ZodObject) && typeof schema !== "function") {
|
|
@@ -28,6 +28,10 @@ export declare const aiAgentOptionsSchema: z.ZodObject<{
|
|
|
28
28
|
memory: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodAny, z.ZodAny]>>;
|
|
29
29
|
}, "strip", z.ZodTypeAny, {
|
|
30
30
|
description?: string | undefined;
|
|
31
|
+
memory?: any;
|
|
32
|
+
includeInputInOutput?: boolean | undefined;
|
|
33
|
+
subscribeTopic?: string | string[] | undefined;
|
|
34
|
+
publishTopic?: string | string[] | ((...args: unknown[]) => unknown) | undefined;
|
|
31
35
|
tools?: (Agent<Message, Message> | ((...args: unknown[]) => unknown))[] | undefined;
|
|
32
36
|
toolChoice?: Agent<Message, Message> | "auto" | "none" | "required" | "router" | undefined;
|
|
33
37
|
name?: string | undefined;
|
|
@@ -36,13 +40,13 @@ export declare const aiAgentOptionsSchema: z.ZodObject<{
|
|
|
36
40
|
outputKey?: string | undefined;
|
|
37
41
|
enableHistory?: boolean | undefined;
|
|
38
42
|
maxHistoryMessages?: number | undefined;
|
|
39
|
-
includeInputInOutput?: boolean | undefined;
|
|
40
|
-
subscribeTopic?: string | string[] | undefined;
|
|
41
|
-
publishTopic?: string | string[] | ((...args: unknown[]) => unknown) | undefined;
|
|
42
43
|
disableLogging?: boolean | undefined;
|
|
43
|
-
memory?: any;
|
|
44
44
|
}, {
|
|
45
45
|
description?: string | undefined;
|
|
46
|
+
memory?: any;
|
|
47
|
+
includeInputInOutput?: boolean | undefined;
|
|
48
|
+
subscribeTopic?: string | string[] | undefined;
|
|
49
|
+
publishTopic?: string | string[] | ((...args: unknown[]) => unknown) | undefined;
|
|
46
50
|
tools?: (Agent<Message, Message> | ((...args: unknown[]) => unknown))[] | undefined;
|
|
47
51
|
toolChoice?: Agent<Message, Message> | "auto" | "none" | "required" | "router" | undefined;
|
|
48
52
|
name?: string | undefined;
|
|
@@ -51,11 +55,7 @@ export declare const aiAgentOptionsSchema: z.ZodObject<{
|
|
|
51
55
|
outputKey?: string | undefined;
|
|
52
56
|
enableHistory?: boolean | undefined;
|
|
53
57
|
maxHistoryMessages?: number | undefined;
|
|
54
|
-
includeInputInOutput?: boolean | undefined;
|
|
55
|
-
subscribeTopic?: string | string[] | undefined;
|
|
56
|
-
publishTopic?: string | string[] | ((...args: unknown[]) => unknown) | undefined;
|
|
57
58
|
disableLogging?: boolean | undefined;
|
|
58
|
-
memory?: any;
|
|
59
59
|
}>;
|
|
60
60
|
export declare class AIAgent<I extends Message = Message, O extends Message = Message> extends Agent<I, O> {
|
|
61
61
|
static from<I extends Message, O extends Message>(options: AIAgentOptions<I, O>): AIAgent<I, O>;
|
|
@@ -59,7 +59,10 @@ export class AIAgent extends Agent {
|
|
|
59
59
|
const toolsMap = new Map(toolAgents?.map((i) => [i.name, i]));
|
|
60
60
|
const toolCallMessages = [];
|
|
61
61
|
for (;;) {
|
|
62
|
-
const { text, json, toolCalls } = await
|
|
62
|
+
const { text, json, toolCalls } = await context.call(model, {
|
|
63
|
+
...modelInput,
|
|
64
|
+
messages: messages.concat(toolCallMessages),
|
|
65
|
+
});
|
|
63
66
|
if (toolCalls?.length) {
|
|
64
67
|
const executedToolCalls = [];
|
|
65
68
|
// Execute tools
|
|
@@ -68,7 +71,7 @@ export class AIAgent extends Agent {
|
|
|
68
71
|
if (!tool)
|
|
69
72
|
throw new Error(`Tool not found: ${call.function.name}`);
|
|
70
73
|
// NOTE: should pass both arguments (model generated) and input (user provided) to the tool
|
|
71
|
-
const output = await
|
|
74
|
+
const output = await context.call(tool, { ...call.function.arguments, ...input }, { disableTransfer: true });
|
|
72
75
|
// NOTE: Return transfer output immediately
|
|
73
76
|
if (isTransferAgentOutput(output)) {
|
|
74
77
|
return output;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Client, type ClientOptions } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import { type SSEClientTransportOptions } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
2
3
|
import { type StdioServerParameters } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3
4
|
import type { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
4
5
|
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
@@ -15,6 +16,15 @@ export interface MCPAgentOptions extends AgentOptions {
|
|
|
15
16
|
export type MCPServerOptions = SSEServerParameters | StdioServerParameters;
|
|
16
17
|
export type SSEServerParameters = {
|
|
17
18
|
url: string;
|
|
19
|
+
/**
|
|
20
|
+
* Additional options to pass to the SSEClientTransport.
|
|
21
|
+
*/
|
|
22
|
+
opts?: SSEClientTransportOptions;
|
|
23
|
+
/**
|
|
24
|
+
* The timeout for requests to the server, in milliseconds.
|
|
25
|
+
* @default 60000
|
|
26
|
+
*/
|
|
27
|
+
timeout?: number;
|
|
18
28
|
/**
|
|
19
29
|
* Whether to automatically reconnect to the server if the connection is lost.
|
|
20
30
|
* @default 10 set to 0 to disable automatic reconnection
|
|
@@ -31,7 +41,7 @@ export declare class MCPAgent extends Agent {
|
|
|
31
41
|
static from(options: MCPAgentOptions): MCPAgent;
|
|
32
42
|
private static fromTransport;
|
|
33
43
|
constructor(options: MCPAgentOptions);
|
|
34
|
-
|
|
44
|
+
client: Client;
|
|
35
45
|
readonly prompts: MCPPrompt[] & {
|
|
36
46
|
[key: string]: MCPPrompt;
|
|
37
47
|
};
|
|
@@ -44,6 +54,7 @@ export declare class MCPAgent extends Agent {
|
|
|
44
54
|
}
|
|
45
55
|
export interface ClientWithReconnectOptions {
|
|
46
56
|
transportCreator?: () => PromiseOrValue<Transport>;
|
|
57
|
+
timeout?: number;
|
|
47
58
|
maxReconnects?: number;
|
|
48
59
|
shouldReconnect?: (error: Error) => boolean;
|
|
49
60
|
}
|
|
@@ -60,7 +71,6 @@ export interface MCPBaseOptions<I extends Message = Message, O extends Message =
|
|
|
60
71
|
export declare abstract class MCPBase<I extends Message, O extends Message> extends Agent<I, O> {
|
|
61
72
|
constructor(options: MCPBaseOptions<I, O>);
|
|
62
73
|
protected client: ClientWithReconnect;
|
|
63
|
-
protected get mcpServer(): string | undefined;
|
|
64
74
|
}
|
|
65
75
|
export declare class MCPTool extends MCPBase<Message, CallToolResult> {
|
|
66
76
|
process(input: Message): Promise<CallToolResult>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
-
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
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
5
|
import pRetry from "p-retry";
|
|
@@ -11,7 +11,11 @@ import { Agent } from "./agent.js";
|
|
|
11
11
|
const MCP_AGENT_CLIENT_NAME = "MCPAgent";
|
|
12
12
|
const MCP_AGENT_CLIENT_VERSION = "0.0.1";
|
|
13
13
|
const DEFAULT_MAX_RECONNECTS = 10;
|
|
14
|
-
const
|
|
14
|
+
const DEFAULT_TIMEOUT = () => z.coerce
|
|
15
|
+
.number()
|
|
16
|
+
.int()
|
|
17
|
+
.min(0)
|
|
18
|
+
.safeParse(process.env.MCP_TIMEOUT || process.env.TIMEOUT).data || 60e3;
|
|
15
19
|
function isSSEServerParameters(options) {
|
|
16
20
|
return "url" in options && typeof options.url === "string";
|
|
17
21
|
}
|
|
@@ -31,7 +35,7 @@ export class MCPAgent extends Agent {
|
|
|
31
35
|
static from(options) {
|
|
32
36
|
checkArguments("MCPAgent.from", mcpAgentOptionsSchema, options);
|
|
33
37
|
if (isSSEServerParameters(options)) {
|
|
34
|
-
const transport = () => new SSEClientTransport(new URL(options.url));
|
|
38
|
+
const transport = () => new SSEClientTransport(new URL(options.url), options.opts);
|
|
35
39
|
return MCPAgent.fromTransport(transport, options);
|
|
36
40
|
}
|
|
37
41
|
if (isStdioServerParameters(options)) {
|
|
@@ -53,29 +57,36 @@ export class MCPAgent extends Agent {
|
|
|
53
57
|
version: MCP_AGENT_CLIENT_VERSION,
|
|
54
58
|
}, undefined, isSSEServerParameters(options) ? { transportCreator, ...options } : undefined);
|
|
55
59
|
const transport = transportCreator();
|
|
56
|
-
|
|
60
|
+
logger.mcp(`Connecting to MCP server: ${getMCPServerString(options)}`);
|
|
61
|
+
await client.connect(transport);
|
|
57
62
|
const mcpServer = getMCPServerName(client);
|
|
58
63
|
const { tools: isToolsAvailable, prompts: isPromptsAvailable, resources: isResourcesAvailable, } = client.getServerCapabilities() ?? {};
|
|
64
|
+
logger.mcp(`Listing tools from ${mcpServer}`);
|
|
59
65
|
const tools = isToolsAvailable
|
|
60
|
-
? await
|
|
61
|
-
.
|
|
62
|
-
|
|
66
|
+
? await client.listTools().then(({ tools }) => {
|
|
67
|
+
logger.mcp(`Listing tools from ${mcpServer} completed %O`, tools?.map((i) => i.name));
|
|
68
|
+
return tools.map((tool) => toolFromMCPTool(tool, { client }));
|
|
69
|
+
})
|
|
63
70
|
: undefined;
|
|
71
|
+
logger.mcp(`Listing prompts from ${mcpServer}`);
|
|
64
72
|
const prompts = isPromptsAvailable
|
|
65
|
-
? await
|
|
66
|
-
.
|
|
67
|
-
|
|
73
|
+
? await client.listPrompts().then(({ prompts }) => {
|
|
74
|
+
logger.mcp(`Listing prompts from ${mcpServer} completed %O`, prompts?.map((i) => i.name));
|
|
75
|
+
return prompts.map((prompt) => promptFromMCPPrompt(prompt, { client }));
|
|
76
|
+
})
|
|
68
77
|
: undefined;
|
|
78
|
+
logger.mcp(`Listing resources from ${mcpServer}`);
|
|
79
|
+
// TODO: should conditionally call listResourceTemplates based on the server capabilities
|
|
80
|
+
// but the capability is not correct in the current SDK version
|
|
69
81
|
const resources = isResourcesAvailable
|
|
70
|
-
? await
|
|
71
|
-
.spinner(
|
|
72
|
-
// TODO: should conditionally call listResourceTemplates based on the server capabilities
|
|
73
|
-
// but the capability is not correct in the current SDK version
|
|
74
|
-
Promise.all([
|
|
82
|
+
? await Promise.all([
|
|
75
83
|
client.listResources().catch(() => ({ resources: [] })),
|
|
76
84
|
client.listResourceTemplates().catch(() => ({ resourceTemplates: [] })),
|
|
77
|
-
])
|
|
78
|
-
|
|
85
|
+
]).then(([{ resources }, { resourceTemplates }]) => {
|
|
86
|
+
const result = [...resources, ...resourceTemplates].map((resource) => resourceFromMCPResource(resource, { client }));
|
|
87
|
+
logger.mcp(`Listing resources from ${mcpServer} completed %O`, result.map((i) => i.name));
|
|
88
|
+
return result;
|
|
89
|
+
})
|
|
79
90
|
: undefined;
|
|
80
91
|
return new MCPAgent({
|
|
81
92
|
name: client.getServerVersion()?.name,
|
|
@@ -127,22 +138,28 @@ class ClientWithReconnect extends Client {
|
|
|
127
138
|
throw new Error("reconnect requires a transportCreator");
|
|
128
139
|
await pRetry(async () => {
|
|
129
140
|
await this.close();
|
|
130
|
-
await this.connect(await transportCreator()
|
|
141
|
+
await this.connect(await transportCreator(), {
|
|
142
|
+
timeout: this.reconnectOptions?.timeout ?? DEFAULT_TIMEOUT(),
|
|
143
|
+
});
|
|
131
144
|
}, {
|
|
132
145
|
retries: this.reconnectOptions?.maxReconnects ?? DEFAULT_MAX_RECONNECTS,
|
|
133
146
|
shouldRetry: this.shouldReconnect,
|
|
134
|
-
onFailedAttempt: (error) =>
|
|
147
|
+
onFailedAttempt: (error) => logger.mcp("Reconnect attempt failed: %O", error),
|
|
135
148
|
});
|
|
136
149
|
}
|
|
137
150
|
async request(request, resultSchema, options) {
|
|
151
|
+
const mergedOptions = {
|
|
152
|
+
...options,
|
|
153
|
+
timeout: options?.timeout ?? DEFAULT_TIMEOUT(),
|
|
154
|
+
};
|
|
138
155
|
try {
|
|
139
|
-
return await super.request(request, resultSchema,
|
|
156
|
+
return await super.request(request, resultSchema, mergedOptions);
|
|
140
157
|
}
|
|
141
158
|
catch (error) {
|
|
142
159
|
if (this.shouldReconnect(error)) {
|
|
143
|
-
|
|
160
|
+
logger.mcp("Error occurred, reconnecting to MCP server: %O", error);
|
|
144
161
|
await this.reconnect();
|
|
145
|
-
return await super.request(request, resultSchema,
|
|
162
|
+
return await super.request(request, resultSchema, mergedOptions);
|
|
146
163
|
}
|
|
147
164
|
throw error;
|
|
148
165
|
}
|
|
@@ -154,19 +171,16 @@ export class MCPBase extends Agent {
|
|
|
154
171
|
this.client = options.client;
|
|
155
172
|
}
|
|
156
173
|
client;
|
|
157
|
-
get mcpServer() {
|
|
158
|
-
return getMCPServerName(this.client);
|
|
159
|
-
}
|
|
160
174
|
}
|
|
161
175
|
export class MCPTool extends MCPBase {
|
|
162
176
|
async process(input) {
|
|
163
|
-
const result = await
|
|
177
|
+
const result = await this.client.callTool({ name: this.name, arguments: input });
|
|
164
178
|
return result;
|
|
165
179
|
}
|
|
166
180
|
}
|
|
167
181
|
export class MCPPrompt extends MCPBase {
|
|
168
182
|
async process(input) {
|
|
169
|
-
const result = await
|
|
183
|
+
const result = await this.client.getPrompt({ name: this.name, arguments: input });
|
|
170
184
|
return result;
|
|
171
185
|
}
|
|
172
186
|
}
|
|
@@ -178,7 +192,7 @@ export class MCPResource extends MCPBase {
|
|
|
178
192
|
uri;
|
|
179
193
|
async process(input) {
|
|
180
194
|
const uri = new UriTemplate(this.uri).expand(input);
|
|
181
|
-
const result = await
|
|
195
|
+
const result = await this.client.readResource({ uri });
|
|
182
196
|
return result;
|
|
183
197
|
}
|
|
184
198
|
}
|
|
@@ -197,6 +211,10 @@ const mcpAgentOptionsSchema = z.union([
|
|
|
197
211
|
}),
|
|
198
212
|
z.object({
|
|
199
213
|
url: z.string(),
|
|
214
|
+
opts: z.object({}).optional(),
|
|
215
|
+
timeout: z.number().optional(),
|
|
216
|
+
maxReconnects: z.number().optional(),
|
|
217
|
+
shouldReconnect: z.function().args(z.instanceof(Error)).returns(z.boolean()).optional(),
|
|
200
218
|
}),
|
|
201
219
|
z.object({
|
|
202
220
|
command: z.string(),
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import { ReadableStream } from "node:stream/web";
|
|
2
|
-
import type
|
|
3
|
-
import type { MessagePayload
|
|
2
|
+
import { type Context, type Runnable } from "../execution-engine/context.js";
|
|
3
|
+
import type { MessagePayload } from "../execution-engine/message-queue.js";
|
|
4
4
|
import { type PromiseOrValue } from "../utils/type-utils.js";
|
|
5
5
|
import { Agent, type AgentOptions, type Message } from "./agent.js";
|
|
6
6
|
export interface UserAgentOptions<I extends Message = Message, O extends Message = Message> extends AgentOptions<I, O> {
|
|
7
7
|
context: Context;
|
|
8
8
|
process?: (input: I, context: Context) => PromiseOrValue<O>;
|
|
9
|
+
activeAgent?: Runnable;
|
|
9
10
|
}
|
|
10
11
|
export declare class UserAgent<I extends Message = Message, O extends Message = Message> extends Agent<I, O> {
|
|
11
12
|
static from<I extends Message, O extends Message>(options: UserAgentOptions<I, O>): UserAgent<I, O>;
|
|
12
13
|
constructor(options: UserAgentOptions<I, O>);
|
|
13
|
-
|
|
14
|
-
private get ctx();
|
|
14
|
+
context: Context;
|
|
15
15
|
private _process?;
|
|
16
|
+
private activeAgent?;
|
|
16
17
|
call(input: string | I, context?: Context): Promise<O>;
|
|
17
18
|
process(input: I, context: Context): Promise<O>;
|
|
18
|
-
publish
|
|
19
|
-
subscribe
|
|
20
|
-
|
|
21
|
-
subscribe(topic: string, listener?: MessageQueueListener): Unsubscribe | Promise<MessagePayload>;
|
|
22
|
-
unsubscribe(topic: string, listener: MessageQueueListener): void;
|
|
19
|
+
publish: Context["publish"];
|
|
20
|
+
subscribe: Context["subscribe"];
|
|
21
|
+
unsubscribe: Context["unsubscribe"];
|
|
23
22
|
get stream(): ReadableStream<MessagePayload & {
|
|
24
23
|
topic: string;
|
|
25
24
|
}>;
|
|
25
|
+
protected checkUsageAgentCalls(_context: Context): void;
|
|
26
26
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ReadableStream } from "node:stream/web";
|
|
2
|
+
import { createPublishMessage } from "../execution-engine/context.js";
|
|
2
3
|
import { orArrayToArray } from "../utils/type-utils.js";
|
|
3
4
|
import { Agent } from "./agent.js";
|
|
4
5
|
export class UserAgent extends Agent {
|
|
@@ -6,40 +7,46 @@ export class UserAgent extends Agent {
|
|
|
6
7
|
return new UserAgent(options);
|
|
7
8
|
}
|
|
8
9
|
constructor(options) {
|
|
9
|
-
super({ ...options,
|
|
10
|
+
super({ ...options, disableEvents: true });
|
|
10
11
|
this._process = options.process;
|
|
11
12
|
this.context = options.context;
|
|
13
|
+
this.activeAgent = options.activeAgent;
|
|
12
14
|
}
|
|
13
15
|
context;
|
|
14
|
-
get ctx() {
|
|
15
|
-
if (!this.context)
|
|
16
|
-
throw new Error("UserAgent must have a context");
|
|
17
|
-
return this.context;
|
|
18
|
-
}
|
|
19
16
|
_process;
|
|
17
|
+
activeAgent;
|
|
20
18
|
call(input, context) {
|
|
19
|
+
if (!context)
|
|
20
|
+
this.context = this.context.newContext({ reset: true });
|
|
21
21
|
return super.call(input, context ?? this.context);
|
|
22
22
|
}
|
|
23
23
|
async process(input, context) {
|
|
24
24
|
if (this._process) {
|
|
25
25
|
return this._process(input, context);
|
|
26
26
|
}
|
|
27
|
+
if (this.activeAgent) {
|
|
28
|
+
const [output, agent] = await context.call(this.activeAgent, input, {
|
|
29
|
+
returnActiveAgent: true,
|
|
30
|
+
});
|
|
31
|
+
this.activeAgent = agent;
|
|
32
|
+
return output;
|
|
33
|
+
}
|
|
27
34
|
const publicTopic = typeof this.publishTopic === "function" ? await this.publishTopic(input) : this.publishTopic;
|
|
28
35
|
if (publicTopic?.length) {
|
|
29
|
-
context.publish(publicTopic, input, this);
|
|
36
|
+
context.publish(publicTopic, createPublishMessage(input, this));
|
|
30
37
|
return {};
|
|
31
38
|
}
|
|
32
39
|
throw new Error("UserAgent must have a process function or a publishTopic");
|
|
33
40
|
}
|
|
34
|
-
publish(
|
|
35
|
-
return this.
|
|
36
|
-
}
|
|
37
|
-
subscribe(
|
|
38
|
-
return this.
|
|
39
|
-
}
|
|
40
|
-
unsubscribe(
|
|
41
|
-
this.
|
|
42
|
-
}
|
|
41
|
+
publish = ((...args) => {
|
|
42
|
+
return this.context.publish(...args);
|
|
43
|
+
});
|
|
44
|
+
subscribe = ((...args) => {
|
|
45
|
+
return this.context.subscribe(...args);
|
|
46
|
+
});
|
|
47
|
+
unsubscribe = ((...args) => {
|
|
48
|
+
this.context.unsubscribe(...args);
|
|
49
|
+
});
|
|
43
50
|
get stream() {
|
|
44
51
|
let subscriptions = [];
|
|
45
52
|
return new ReadableStream({
|
|
@@ -56,4 +63,7 @@ export class UserAgent extends Agent {
|
|
|
56
63
|
},
|
|
57
64
|
});
|
|
58
65
|
}
|
|
66
|
+
checkUsageAgentCalls(_context) {
|
|
67
|
+
// ignore calls usage check for UserAgent
|
|
68
|
+
}
|
|
59
69
|
}
|
|
@@ -2,19 +2,36 @@ import EventEmitter from "node:events";
|
|
|
2
2
|
import { Agent, type FunctionAgentFn, type Message } from "../agents/agent.js";
|
|
3
3
|
import { UserAgent } from "../agents/user-agent.js";
|
|
4
4
|
import type { ChatModel } from "../models/chat-model.js";
|
|
5
|
+
import { type OmitPropertiesFromArrayFirstElement } from "../utils/type-utils.js";
|
|
6
|
+
import type { Args, Listener, TypedEventEmitter } from "../utils/typed-event-emtter.js";
|
|
5
7
|
import { type MessagePayload, MessageQueue, type MessageQueueListener, type Unsubscribe } from "./message-queue.js";
|
|
8
|
+
import { type ContextLimits, type ContextUsage } from "./usage.js";
|
|
6
9
|
export type Runnable<I extends Message = Message, O extends Message = Message> = Agent<I, O> | FunctionAgentFn;
|
|
7
|
-
export interface
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
export interface AgentEvent {
|
|
11
|
+
parentContextId?: string;
|
|
12
|
+
contextId: string;
|
|
13
|
+
timestamp: number;
|
|
14
|
+
agent: Agent;
|
|
11
15
|
}
|
|
12
|
-
export interface
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
export interface ContextEventMap {
|
|
17
|
+
agentStarted: [AgentEvent & {
|
|
18
|
+
input: Message;
|
|
19
|
+
}];
|
|
20
|
+
agentSucceed: [AgentEvent & {
|
|
21
|
+
output: Message;
|
|
22
|
+
}];
|
|
23
|
+
agentFailed: [AgentEvent & {
|
|
24
|
+
error: Error;
|
|
25
|
+
}];
|
|
16
26
|
}
|
|
17
|
-
export
|
|
27
|
+
export type ContextEmitEventMap = {
|
|
28
|
+
[K in keyof ContextEventMap]: OmitPropertiesFromArrayFirstElement<ContextEventMap[K], "contextId" | "parentContextId" | "timestamp">;
|
|
29
|
+
};
|
|
30
|
+
export interface CallOptions {
|
|
31
|
+
returnActiveAgent?: boolean;
|
|
32
|
+
disableTransfer?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface Context extends TypedEventEmitter<ContextEventMap, ContextEmitEventMap> {
|
|
18
35
|
model?: ChatModel;
|
|
19
36
|
tools?: Agent[];
|
|
20
37
|
usage: ContextUsage;
|
|
@@ -26,13 +43,6 @@ export interface Context extends EventEmitter {
|
|
|
26
43
|
* @returns User agent
|
|
27
44
|
*/
|
|
28
45
|
call<I extends Message, O extends Message>(agent: Runnable<I, O>): UserAgent<I, O>;
|
|
29
|
-
/**
|
|
30
|
-
* Call an agent with a message
|
|
31
|
-
* @param agent Agent to call
|
|
32
|
-
* @param message Message to pass to the agent
|
|
33
|
-
* @returns the output of the agent
|
|
34
|
-
*/
|
|
35
|
-
call<I extends Message, O extends Message>(agent: Runnable<I, O>, message: I | string): Promise<O>;
|
|
36
46
|
/**
|
|
37
47
|
* Call an agent with a message and return the output and the active agent
|
|
38
48
|
* @param agent Agent to call
|
|
@@ -40,54 +50,82 @@ export interface Context extends EventEmitter {
|
|
|
40
50
|
* @param options.returnActiveAgent return the active agent
|
|
41
51
|
* @returns the output of the agent and the final active agent
|
|
42
52
|
*/
|
|
43
|
-
call<I extends Message, O extends Message>(agent: Runnable<I, O>, message: I | string, options: {
|
|
53
|
+
call<I extends Message, O extends Message>(agent: Runnable<I, O>, message: I | string, options: CallOptions & {
|
|
44
54
|
returnActiveAgent: true;
|
|
45
55
|
}): Promise<[O, Runnable]>;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Call an agent with a message
|
|
58
|
+
* @param agent Agent to call
|
|
59
|
+
* @param message Message to pass to the agent
|
|
60
|
+
* @returns the output of the agent
|
|
61
|
+
*/
|
|
62
|
+
call<I extends Message, O extends Message>(agent: Runnable<I, O>, message: I | string, options?: CallOptions): Promise<O>;
|
|
63
|
+
call<I extends Message, O extends Message>(agent: Runnable<I, O>, message?: I | string, options?: CallOptions): UserAgent<I, O> | Promise<O | [O, Runnable]>;
|
|
49
64
|
/**
|
|
50
65
|
* Publish a message to a topic, the engine will call the listeners of the topic
|
|
51
66
|
* @param topic topic name, or an array of topic names
|
|
52
|
-
* @param
|
|
53
|
-
* @param from the agent who publish the message, if not provided, it will be treated as a user message
|
|
67
|
+
* @param payload message to publish
|
|
54
68
|
*/
|
|
55
|
-
publish(topic: string | string[],
|
|
69
|
+
publish(topic: string | string[], payload: Omit<MessagePayload, "context">): void;
|
|
56
70
|
subscribe(topic: string, listener?: undefined): Promise<MessagePayload>;
|
|
57
71
|
subscribe(topic: string, listener: MessageQueueListener): Unsubscribe;
|
|
58
72
|
subscribe(topic: string, listener?: MessageQueueListener): Unsubscribe | Promise<MessagePayload>;
|
|
73
|
+
subscribe(topic: string, listener?: MessageQueueListener): Unsubscribe | Promise<MessagePayload>;
|
|
59
74
|
unsubscribe(topic: string, listener: MessageQueueListener): void;
|
|
75
|
+
/**
|
|
76
|
+
* Create a child context with the same configuration as the parent context.
|
|
77
|
+
* If `reset` is true, the child context will have a new state (such as: usage).
|
|
78
|
+
*
|
|
79
|
+
* @param options
|
|
80
|
+
* @param options.reset create a new context with initial state (such as: usage)
|
|
81
|
+
* @returns new context
|
|
82
|
+
*/
|
|
83
|
+
newContext(options?: {
|
|
84
|
+
reset?: boolean;
|
|
85
|
+
}): Context;
|
|
86
|
+
}
|
|
87
|
+
export declare function createPublishMessage(message: string | Message, from?: Agent): Omit<MessagePayload, "context">;
|
|
88
|
+
export declare class ExecutionContext implements Context {
|
|
89
|
+
constructor(parent?: ConstructorParameters<typeof ExecutionContextInternal>[0]);
|
|
90
|
+
parentId?: string;
|
|
91
|
+
id: string;
|
|
92
|
+
readonly internal: ExecutionContextInternal;
|
|
93
|
+
get model(): ChatModel | undefined;
|
|
94
|
+
get tools(): Agent<Message, Message>[] | undefined;
|
|
95
|
+
get limits(): ContextLimits | undefined;
|
|
96
|
+
get status(): "normal" | "timeout";
|
|
97
|
+
get usage(): ContextUsage;
|
|
98
|
+
newContext({ reset }?: {
|
|
99
|
+
reset?: boolean;
|
|
100
|
+
}): ExecutionContext;
|
|
101
|
+
call: Context["call"];
|
|
102
|
+
publish: Context["publish"];
|
|
103
|
+
subscribe: Context["subscribe"];
|
|
104
|
+
unsubscribe: Context["unsubscribe"];
|
|
105
|
+
emit<K extends keyof ContextEmitEventMap>(eventName: K, ...args: Args<K, ContextEmitEventMap>): boolean;
|
|
106
|
+
on<K extends keyof ContextEventMap>(eventName: K, listener: Listener<K, ContextEventMap>): this;
|
|
107
|
+
once<K extends keyof ContextEventMap>(eventName: K, listener: Listener<K, ContextEventMap>): this;
|
|
108
|
+
off<K extends keyof ContextEventMap>(eventName: K, listener: Listener<K, ContextEventMap>): this;
|
|
60
109
|
}
|
|
61
|
-
|
|
62
|
-
private readonly
|
|
63
|
-
constructor(
|
|
64
|
-
model?: ChatModel;
|
|
65
|
-
tools?: Agent[];
|
|
66
|
-
limits?: ContextLimits;
|
|
110
|
+
declare class ExecutionContextInternal {
|
|
111
|
+
private readonly parent?;
|
|
112
|
+
constructor(parent?: (Pick<Context, "model" | "tools" | "limits"> & {
|
|
67
113
|
messageQueue?: MessageQueue;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
114
|
+
}) | undefined);
|
|
115
|
+
readonly messageQueue: MessageQueue;
|
|
116
|
+
readonly events: EventEmitter<ContextEventMap>;
|
|
71
117
|
get model(): ChatModel | undefined;
|
|
72
118
|
get tools(): Agent<Message, Message>[] | undefined;
|
|
119
|
+
get limits(): ContextLimits | undefined;
|
|
73
120
|
usage: ContextUsage;
|
|
74
|
-
limits?: ContextLimits | undefined;
|
|
75
121
|
private abortController;
|
|
76
122
|
private timer?;
|
|
77
123
|
private initTimeout;
|
|
78
124
|
get status(): "normal" | "timeout";
|
|
79
|
-
call<I extends Message, O extends Message>(agent: Runnable<I, O
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}): Promise<[O, Runnable]>;
|
|
84
|
-
call<I extends Message, O extends Message>(agent: Runnable<I, O>, message?: I | string, options?: {
|
|
85
|
-
returnActiveAgent?: boolean;
|
|
86
|
-
}): UserAgent<I, O> | Promise<O | [O, Runnable]>;
|
|
87
|
-
publish(topic: string | string[], message: Message | string, from?: Agent): void;
|
|
88
|
-
subscribe(topic: string, listener?: undefined): Promise<MessagePayload>;
|
|
89
|
-
subscribe(topic: string, listener: MessageQueueListener): Unsubscribe;
|
|
90
|
-
unsubscribe(topic: string, listener: MessageQueueListener): void;
|
|
91
|
-
emit(eventName: string | symbol, ...args: unknown[]): boolean;
|
|
125
|
+
call<I extends Message, O extends Message>(agent: Runnable<I, O>, input: I, context: Context, options?: CallOptions): Promise<{
|
|
126
|
+
agent: Runnable;
|
|
127
|
+
output: O;
|
|
128
|
+
}>;
|
|
92
129
|
private callAgent;
|
|
93
130
|
}
|
|
131
|
+
export {};
|