@ai-zen/agents-sdk 0.2.0-alpha.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.
- package/LICENSE +21 -0
- package/README.md +100 -0
- package/dist/capabilities/Capabilities.d.ts +71 -0
- package/dist/capabilities/Capabilities.js +161 -0
- package/dist/capabilities/PermissionEvaluator.d.ts +19 -0
- package/dist/capabilities/PermissionEvaluator.js +50 -0
- package/dist/capabilities/disclosure.d.ts +10 -0
- package/dist/capabilities/disclosure.js +17 -0
- package/dist/capabilities/discovery/builtin.d.ts +8 -0
- package/dist/capabilities/discovery/builtin.js +17 -0
- package/dist/capabilities/discovery/mcp.d.ts +7 -0
- package/dist/capabilities/discovery/mcp.js +29 -0
- package/dist/capabilities/discovery/skills.d.ts +43 -0
- package/dist/capabilities/discovery/skills.js +175 -0
- package/dist/capabilities/discovery/subagents.d.ts +7 -0
- package/dist/capabilities/discovery/subagents.js +39 -0
- package/dist/capabilities/discovery/usertools.d.ts +13 -0
- package/dist/capabilities/discovery/usertools.js +136 -0
- package/dist/capabilities/implements/builtin/batchEdit.d.ts +2 -0
- package/dist/capabilities/implements/builtin/batchEdit.js +76 -0
- package/dist/capabilities/implements/builtin/copy.d.ts +2 -0
- package/dist/capabilities/implements/builtin/copy.js +38 -0
- package/dist/capabilities/implements/builtin/cwd.d.ts +2 -0
- package/dist/capabilities/implements/builtin/cwd.js +15 -0
- package/dist/capabilities/implements/builtin/downloadFile.d.ts +2 -0
- package/dist/capabilities/implements/builtin/downloadFile.js +70 -0
- package/dist/capabilities/implements/builtin/edit.d.ts +2 -0
- package/dist/capabilities/implements/builtin/edit.js +79 -0
- package/dist/capabilities/implements/builtin/exec.d.ts +2 -0
- package/dist/capabilities/implements/builtin/exec.js +31 -0
- package/dist/capabilities/implements/builtin/exist.d.ts +2 -0
- package/dist/capabilities/implements/builtin/exist.js +25 -0
- package/dist/capabilities/implements/builtin/findText.d.ts +2 -0
- package/dist/capabilities/implements/builtin/findText.js +86 -0
- package/dist/capabilities/implements/builtin/generateImage.d.ts +9 -0
- package/dist/capabilities/implements/builtin/generateImage.js +112 -0
- package/dist/capabilities/implements/builtin/glob.d.ts +2 -0
- package/dist/capabilities/implements/builtin/glob.js +45 -0
- package/dist/capabilities/implements/builtin/index.d.ts +19 -0
- package/dist/capabilities/implements/builtin/index.js +34 -0
- package/dist/capabilities/implements/builtin/ls.d.ts +2 -0
- package/dist/capabilities/implements/builtin/ls.js +28 -0
- package/dist/capabilities/implements/builtin/mkdir.d.ts +2 -0
- package/dist/capabilities/implements/builtin/mkdir.js +34 -0
- package/dist/capabilities/implements/builtin/readFile.d.ts +2 -0
- package/dist/capabilities/implements/builtin/readFile.js +31 -0
- package/dist/capabilities/implements/builtin/rename.d.ts +2 -0
- package/dist/capabilities/implements/builtin/rename.js +32 -0
- package/dist/capabilities/implements/builtin/rm.d.ts +2 -0
- package/dist/capabilities/implements/builtin/rm.js +34 -0
- package/dist/capabilities/implements/builtin/writeFile.d.ts +2 -0
- package/dist/capabilities/implements/builtin/writeFile.js +34 -0
- package/dist/capabilities/implements/mcpTools.d.ts +20 -0
- package/dist/capabilities/implements/mcpTools.js +147 -0
- package/dist/capabilities/implements/skillTools.d.ts +15 -0
- package/dist/capabilities/implements/skillTools.js +113 -0
- package/dist/capabilities/implements/subAgentTools.d.ts +11 -0
- package/dist/capabilities/implements/subAgentTools.js +53 -0
- package/dist/config/ConfigManager.d.ts +49 -0
- package/dist/config/ConfigManager.js +126 -0
- package/dist/config/constants.d.ts +20 -0
- package/dist/config/constants.js +185 -0
- package/dist/crud/AgentRepository.d.ts +9 -0
- package/dist/crud/AgentRepository.js +10 -0
- package/dist/crud/ConversationRepository.d.ts +9 -0
- package/dist/crud/ConversationRepository.js +10 -0
- package/dist/crud/DraftRepository.d.ts +15 -0
- package/dist/crud/DraftRepository.js +41 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +37 -0
- package/dist/plugin/AutoDraftPlugin.d.ts +26 -0
- package/dist/plugin/AutoDraftPlugin.js +58 -0
- package/dist/plugin/AutoMigratePlugin.d.ts +25 -0
- package/dist/plugin/AutoMigratePlugin.js +74 -0
- package/dist/plugin/AutoRefreshToolsPlugin.d.ts +12 -0
- package/dist/plugin/AutoRefreshToolsPlugin.js +23 -0
- package/dist/runtime/McpConnectionManager.d.ts +42 -0
- package/dist/runtime/McpConnectionManager.js +238 -0
- package/dist/runtime/Provider.d.ts +44 -0
- package/dist/runtime/Provider.js +38 -0
- package/dist/runtime/SdkAgent.d.ts +100 -0
- package/dist/runtime/SdkAgent.js +103 -0
- package/dist/runtime/TaskMigrationService.d.ts +24 -0
- package/dist/runtime/TaskMigrationService.js +71 -0
- package/dist/runtime/createAgent.d.ts +14 -0
- package/dist/runtime/createAgent.js +41 -0
- package/dist/runtime/createModel.d.ts +11 -0
- package/dist/runtime/createModel.js +28 -0
- package/dist/shared/EntityRepository.d.ts +24 -0
- package/dist/shared/EntityRepository.js +62 -0
- package/dist/shared/errors.d.ts +8 -0
- package/dist/shared/errors.js +15 -0
- package/dist/shared/logger.d.ts +10 -0
- package/dist/shared/logger.js +10 -0
- package/dist/types/index.d.ts +178 -0
- package/dist/types/index.js +5 -0
- package/package.json +47 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
4
|
+
const DEFAULT_IDLE_TIMEOUT = {
|
|
5
|
+
stdio: 30 * 60 * 1000,
|
|
6
|
+
http: 5 * 60 * 1000,
|
|
7
|
+
};
|
|
8
|
+
/** 指数退避: 1s → 2s → 4s → 8s → 16s → 30s(封顶) */
|
|
9
|
+
function backoff(attempt) {
|
|
10
|
+
const ms = Math.min(1000 * Math.pow(2, attempt), 30_000);
|
|
11
|
+
return ms;
|
|
12
|
+
}
|
|
13
|
+
export class McpConnectionManager {
|
|
14
|
+
servers = new Map();
|
|
15
|
+
/** 可替换的 transport 工厂,用于测试注入 */
|
|
16
|
+
_transportFactory;
|
|
17
|
+
/** 可替换的 Client 工厂,用于测试注入 */
|
|
18
|
+
_clientFactory;
|
|
19
|
+
/**
|
|
20
|
+
* @param transportFactory 可选的自定义 transport 工厂(用于测试注入)
|
|
21
|
+
* @param clientFactory 可选的自定义 Client 工厂(用于测试注入)
|
|
22
|
+
*/
|
|
23
|
+
constructor(transportFactory, clientFactory) {
|
|
24
|
+
this._transportFactory = transportFactory;
|
|
25
|
+
this._clientFactory = clientFactory;
|
|
26
|
+
}
|
|
27
|
+
// ---- 对外查询 ----
|
|
28
|
+
getState(name) {
|
|
29
|
+
return this.servers.get(name)?.state ?? "disconnected";
|
|
30
|
+
}
|
|
31
|
+
getManifest(name) {
|
|
32
|
+
return this.servers.get(name)?.manifest;
|
|
33
|
+
}
|
|
34
|
+
getClient(name) {
|
|
35
|
+
return this.servers.get(name)?.client;
|
|
36
|
+
}
|
|
37
|
+
// ---- 连接 ----
|
|
38
|
+
async connect(name, config, options = {}) {
|
|
39
|
+
const existing = this.servers.get(name);
|
|
40
|
+
if (existing?.state === "connected" && existing.manifest) {
|
|
41
|
+
this.touch(name);
|
|
42
|
+
return existing.manifest;
|
|
43
|
+
}
|
|
44
|
+
if (existing?.state === "connecting" && existing.pendingPromise) {
|
|
45
|
+
await existing.pendingPromise;
|
|
46
|
+
return this.servers.get(name)?.manifest ?? this.throwNotConnected(name);
|
|
47
|
+
}
|
|
48
|
+
const timeoutMs = options.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT[config.transport] ?? 5 * 60 * 1000;
|
|
49
|
+
const pendingPromise = this.connectWithRetry(name, config, options, 0);
|
|
50
|
+
this.servers.set(name, {
|
|
51
|
+
state: "connecting",
|
|
52
|
+
pendingPromise,
|
|
53
|
+
timeoutMs,
|
|
54
|
+
cancelled: false,
|
|
55
|
+
});
|
|
56
|
+
try {
|
|
57
|
+
await pendingPromise;
|
|
58
|
+
const entry = this.servers.get(name);
|
|
59
|
+
this.servers.set(name, {
|
|
60
|
+
...entry,
|
|
61
|
+
state: "connected",
|
|
62
|
+
timeoutTimer: this.scheduleTimeout(name, timeoutMs),
|
|
63
|
+
});
|
|
64
|
+
return entry.manifest;
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
const entry = this.servers.get(name);
|
|
68
|
+
if (entry?.cancelled || entry?.state === "disconnected") {
|
|
69
|
+
this.servers.set(name, { state: "disconnected" });
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
this.servers.set(name, { state: "error" });
|
|
73
|
+
}
|
|
74
|
+
throw err;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async connectWithRetry(name, config, options, attempt) {
|
|
78
|
+
try {
|
|
79
|
+
await this.doConnect(name, config);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
const entry = this.servers.get(name);
|
|
83
|
+
if (entry?.cancelled)
|
|
84
|
+
throw err;
|
|
85
|
+
if (options.isConfigError?.(err))
|
|
86
|
+
throw err;
|
|
87
|
+
if (!options.autoReconnect)
|
|
88
|
+
throw err;
|
|
89
|
+
const maxRetries = options.maxRetries ?? 3;
|
|
90
|
+
if (attempt >= maxRetries)
|
|
91
|
+
throw err;
|
|
92
|
+
const delay = backoff(attempt);
|
|
93
|
+
await new Promise((resolve, reject) => {
|
|
94
|
+
const current = this.servers.get(name);
|
|
95
|
+
if (current)
|
|
96
|
+
current.cancelRetryWait = reject;
|
|
97
|
+
const timer = setTimeout(() => {
|
|
98
|
+
const current2 = this.servers.get(name);
|
|
99
|
+
if (current2)
|
|
100
|
+
current2.cancelRetryWait = undefined;
|
|
101
|
+
if (current2?.cancelled) {
|
|
102
|
+
reject(new Error(`Connection to "${name}" was cancelled`));
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
resolve();
|
|
106
|
+
}
|
|
107
|
+
}, delay);
|
|
108
|
+
const current3 = this.servers.get(name);
|
|
109
|
+
if (current3)
|
|
110
|
+
current3.retryTimer = timer;
|
|
111
|
+
});
|
|
112
|
+
const recheck = this.servers.get(name);
|
|
113
|
+
if (recheck?.cancelled)
|
|
114
|
+
throw new Error(`Connection to "${name}" was cancelled`);
|
|
115
|
+
return this.connectWithRetry(name, config, options, attempt + 1);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async doConnect(name, config) {
|
|
119
|
+
const transport = this.createTransport(config);
|
|
120
|
+
const client = this._clientFactory
|
|
121
|
+
? this._clientFactory()
|
|
122
|
+
: new Client({ name: "ai-zen-agents", version: "0.1.0" }, { capabilities: {} });
|
|
123
|
+
await client.connect(transport);
|
|
124
|
+
const toolsResult = await client.listTools();
|
|
125
|
+
const resourcesResult = await client.listResources();
|
|
126
|
+
const promptsResult = await client.listPrompts();
|
|
127
|
+
const manifest = {
|
|
128
|
+
tools: (toolsResult.tools ?? []).map((t) => ({
|
|
129
|
+
name: t.name,
|
|
130
|
+
description: t.description ?? "",
|
|
131
|
+
inputSchema: t.inputSchema,
|
|
132
|
+
})),
|
|
133
|
+
resources: (resourcesResult.resources ?? []).map((r) => ({
|
|
134
|
+
uri: r.uri,
|
|
135
|
+
name: r.name,
|
|
136
|
+
description: r.description,
|
|
137
|
+
mimeType: r.mimeType,
|
|
138
|
+
})),
|
|
139
|
+
prompts: (promptsResult.prompts ?? []).map((p) => ({
|
|
140
|
+
name: p.name,
|
|
141
|
+
description: p.description,
|
|
142
|
+
})),
|
|
143
|
+
};
|
|
144
|
+
const entry = this.servers.get(name) ?? { state: "connecting" };
|
|
145
|
+
this.servers.set(name, {
|
|
146
|
+
...entry,
|
|
147
|
+
state: "connected",
|
|
148
|
+
client,
|
|
149
|
+
transport,
|
|
150
|
+
manifest,
|
|
151
|
+
cancelled: false,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* 根据配置创建对应的 Transport 实例。
|
|
156
|
+
* 如果设置了自定义工厂(测试用),则使用自定义工厂。
|
|
157
|
+
*/
|
|
158
|
+
createTransport(config) {
|
|
159
|
+
if (this._transportFactory) {
|
|
160
|
+
return this._transportFactory(config);
|
|
161
|
+
}
|
|
162
|
+
if (config.transport === "stdio") {
|
|
163
|
+
if (!config.command) {
|
|
164
|
+
throw new Error(`MCP stdio server 缺少 command`);
|
|
165
|
+
}
|
|
166
|
+
return new StdioClientTransport({
|
|
167
|
+
command: config.command,
|
|
168
|
+
args: config.args,
|
|
169
|
+
env: config.env,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
if (config.transport === "http") {
|
|
173
|
+
if (!config.url) {
|
|
174
|
+
throw new Error(`MCP HTTP server 缺少 url`);
|
|
175
|
+
}
|
|
176
|
+
return new StreamableHTTPClientTransport(new URL(config.url), {
|
|
177
|
+
requestInit: config.headers ? { headers: config.headers } : undefined,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
throw new Error(`不支持的 MCP transport 类型: ${config.transport}`);
|
|
181
|
+
}
|
|
182
|
+
// ---- 断开 ----
|
|
183
|
+
async disconnect(name) {
|
|
184
|
+
const entry = this.servers.get(name);
|
|
185
|
+
if (!entry || entry.state === "disconnected")
|
|
186
|
+
return;
|
|
187
|
+
entry.cancelled = true;
|
|
188
|
+
if (entry.cancelRetryWait) {
|
|
189
|
+
entry.cancelRetryWait(new Error(`Connection to "${name}" was cancelled`));
|
|
190
|
+
entry.cancelRetryWait = undefined;
|
|
191
|
+
}
|
|
192
|
+
if (entry.retryTimer) {
|
|
193
|
+
clearTimeout(entry.retryTimer);
|
|
194
|
+
entry.retryTimer = undefined;
|
|
195
|
+
}
|
|
196
|
+
this.clearTimeout(name);
|
|
197
|
+
try {
|
|
198
|
+
await entry.client?.close();
|
|
199
|
+
await entry.transport?.close();
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
// 断开失败也清理状态
|
|
203
|
+
}
|
|
204
|
+
this.servers.set(name, { state: "disconnected" });
|
|
205
|
+
}
|
|
206
|
+
async disconnectAll() {
|
|
207
|
+
const names = Array.from(this.servers.keys());
|
|
208
|
+
await Promise.all(names.map((n) => this.disconnect(n)));
|
|
209
|
+
}
|
|
210
|
+
// ---- 活跃心跳 ----
|
|
211
|
+
touch(name) {
|
|
212
|
+
const entry = this.servers.get(name);
|
|
213
|
+
if (!entry || entry.state !== "connected" || !entry.timeoutMs)
|
|
214
|
+
return;
|
|
215
|
+
this.clearTimeout(name);
|
|
216
|
+
entry.timeoutTimer = this.scheduleTimeout(name, entry.timeoutMs);
|
|
217
|
+
}
|
|
218
|
+
// ---- 内部 ----
|
|
219
|
+
scheduleTimeout(name, timeoutMs) {
|
|
220
|
+
if (timeoutMs <= 0)
|
|
221
|
+
return undefined;
|
|
222
|
+
const timer = setTimeout(() => {
|
|
223
|
+
this.disconnect(name);
|
|
224
|
+
}, timeoutMs);
|
|
225
|
+
timer.unref();
|
|
226
|
+
return timer;
|
|
227
|
+
}
|
|
228
|
+
clearTimeout(name) {
|
|
229
|
+
const entry = this.servers.get(name);
|
|
230
|
+
if (entry?.timeoutTimer) {
|
|
231
|
+
clearTimeout(entry.timeoutTimer);
|
|
232
|
+
entry.timeoutTimer = undefined;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
throwNotConnected(name) {
|
|
236
|
+
throw new Error(`MCP server "${name}" is not connected`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { AppConfig, McpServerConfig } from "../types/index.js";
|
|
2
|
+
import type { McpConnectionManager } from "./McpConnectionManager.js";
|
|
3
|
+
/**
|
|
4
|
+
* Provider — 全局上下文实例。
|
|
5
|
+
*
|
|
6
|
+
* SDK 不是一个模块工具箱(到处 import 散装函数),而是一个实例,
|
|
7
|
+
* 持有所有全局状态(配置、路径、MCP 管理器等),各层通过它获取所需。
|
|
8
|
+
*
|
|
9
|
+
* 设计原则:
|
|
10
|
+
* - Provider 实例一旦创建不可变(路径、配置等不变),refresh 时创建新实例
|
|
11
|
+
* - 各层不直接 import 散装函数,而是通过 Provider 实例获取
|
|
12
|
+
* - Provider 不关心能力的发现/过滤/实例化,那是 Capabilities 的事
|
|
13
|
+
*/
|
|
14
|
+
export declare class Provider {
|
|
15
|
+
/** 应用配置(端点、模型等) */
|
|
16
|
+
readonly config: AppConfig;
|
|
17
|
+
readonly agentsDir: string;
|
|
18
|
+
readonly subAgentsPaths: string[];
|
|
19
|
+
readonly skillsPaths: string[];
|
|
20
|
+
readonly toolsPaths: string[];
|
|
21
|
+
readonly mcpPaths: string[];
|
|
22
|
+
readonly conversationsDir: string;
|
|
23
|
+
readonly draftsDir: string;
|
|
24
|
+
readonly mcpManager?: McpConnectionManager;
|
|
25
|
+
readonly mcpConfigs?: Map<string, {
|
|
26
|
+
name: string;
|
|
27
|
+
config: McpServerConfig;
|
|
28
|
+
}>;
|
|
29
|
+
constructor(options: {
|
|
30
|
+
config: AppConfig;
|
|
31
|
+
agentsDir: string;
|
|
32
|
+
subAgentsPaths?: string[];
|
|
33
|
+
skillsPaths?: string[];
|
|
34
|
+
toolsPaths?: string[];
|
|
35
|
+
mcpPaths?: string[];
|
|
36
|
+
conversationsDir: string;
|
|
37
|
+
draftsDir: string;
|
|
38
|
+
mcpManager?: McpConnectionManager;
|
|
39
|
+
mcpConfigs?: Map<string, {
|
|
40
|
+
name: string;
|
|
41
|
+
config: McpServerConfig;
|
|
42
|
+
}>;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider — 全局上下文实例。
|
|
3
|
+
*
|
|
4
|
+
* SDK 不是一个模块工具箱(到处 import 散装函数),而是一个实例,
|
|
5
|
+
* 持有所有全局状态(配置、路径、MCP 管理器等),各层通过它获取所需。
|
|
6
|
+
*
|
|
7
|
+
* 设计原则:
|
|
8
|
+
* - Provider 实例一旦创建不可变(路径、配置等不变),refresh 时创建新实例
|
|
9
|
+
* - 各层不直接 import 散装函数,而是通过 Provider 实例获取
|
|
10
|
+
* - Provider 不关心能力的发现/过滤/实例化,那是 Capabilities 的事
|
|
11
|
+
*/
|
|
12
|
+
export class Provider {
|
|
13
|
+
/** 应用配置(端点、模型等) */
|
|
14
|
+
config;
|
|
15
|
+
// ---- 路径 ----
|
|
16
|
+
agentsDir;
|
|
17
|
+
subAgentsPaths;
|
|
18
|
+
skillsPaths;
|
|
19
|
+
toolsPaths;
|
|
20
|
+
mcpPaths;
|
|
21
|
+
conversationsDir;
|
|
22
|
+
draftsDir;
|
|
23
|
+
// ---- MCP ----
|
|
24
|
+
mcpManager;
|
|
25
|
+
mcpConfigs;
|
|
26
|
+
constructor(options) {
|
|
27
|
+
this.config = options.config;
|
|
28
|
+
this.agentsDir = options.agentsDir;
|
|
29
|
+
this.subAgentsPaths = options.subAgentsPaths ?? [];
|
|
30
|
+
this.skillsPaths = options.skillsPaths ?? [];
|
|
31
|
+
this.toolsPaths = options.toolsPaths ?? [];
|
|
32
|
+
this.mcpPaths = options.mcpPaths ?? [];
|
|
33
|
+
this.conversationsDir = options.conversationsDir;
|
|
34
|
+
this.draftsDir = options.draftsDir;
|
|
35
|
+
this.mcpManager = options.mcpManager;
|
|
36
|
+
this.mcpConfigs = options.mcpConfigs;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Agent, type ChatCompletionModel } from "@ai-zen/agents-core";
|
|
2
|
+
import type { AgentNS, Tool } from "@ai-zen/agents-core";
|
|
3
|
+
import type { AgentDefinition, AgentPermissions } from "../types/index.js";
|
|
4
|
+
import type { Capabilities } from "../capabilities/Capabilities.js";
|
|
5
|
+
import type { Provider } from "./Provider.js";
|
|
6
|
+
/**
|
|
7
|
+
* Agent 插件上下文:当前 SdkAgent + 发送内容 + 消息列表。
|
|
8
|
+
*/
|
|
9
|
+
export interface SendContext {
|
|
10
|
+
agent: SdkAgent;
|
|
11
|
+
content: string;
|
|
12
|
+
messages: AgentNS.Message[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Agent 插件接口。
|
|
16
|
+
* 每个插件单一职责,通过钩子介入 Agent 生命周期。
|
|
17
|
+
*
|
|
18
|
+
* onInit: Agent.init() 时调用,用于异步初始化
|
|
19
|
+
* onBeforeSend: Agent.send() 调用前触发。可用于刷新工具列表等。
|
|
20
|
+
* onAfterSend: Agent.send() 返回后调用。可通过 ctx.agent 替换当前 Agent。
|
|
21
|
+
* onInnerLoopStart: Agent 内循环开始前触发。由 Core Agent 在内循环中调用。
|
|
22
|
+
* onInnerLoopEnd: Agent 内循环结束后触发。由 Core Agent 在内循环中调用。
|
|
23
|
+
*/
|
|
24
|
+
export interface AgentPlugin {
|
|
25
|
+
/** Agent.init() 时调用,用于异步初始化 */
|
|
26
|
+
onInit?(): Promise<void>;
|
|
27
|
+
/** Agent.send() 调用前触发 */
|
|
28
|
+
onBeforeSend?(ctx: SendContext): Promise<void>;
|
|
29
|
+
/** Agent.send() 返回后调用,可通过 ctx.agent 替换当前 Agent */
|
|
30
|
+
onAfterSend?(ctx: SendContext): Promise<void>;
|
|
31
|
+
/** Agent 内循环开始前触发 */
|
|
32
|
+
onInnerLoopStart?(ctx: SendContext): Promise<void>;
|
|
33
|
+
/** Agent 内循环结束后触发 */
|
|
34
|
+
onInnerLoopEnd?(ctx: SendContext): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* SDK Agent — 继承 Core Agent,携带 SDK 层额外信息。
|
|
38
|
+
*
|
|
39
|
+
* Core Agent 不做复杂逻辑(不感知权限、不感知文件系统),
|
|
40
|
+
* SdkAgent 在 Core Agent 基础上增加了 SDK 层需要的元数据,
|
|
41
|
+
* 并通过 provider 字段访问全局上下文。
|
|
42
|
+
*
|
|
43
|
+
* 携带:
|
|
44
|
+
* - provider:全局 Provider 实例
|
|
45
|
+
* - definition:Agent 原始定义
|
|
46
|
+
* - permissions:Agent 权限,供 call_skill_sub_agent 等回调读取
|
|
47
|
+
* - caps:Capabilities 全局注册表,供 refreshTools 刷新工具列表
|
|
48
|
+
*
|
|
49
|
+
* 插件能力:
|
|
50
|
+
* - use(plugin):注册插件
|
|
51
|
+
* - init():初始化所有已注册插件
|
|
52
|
+
* - send():重写,在前后执行插件钩子
|
|
53
|
+
*/
|
|
54
|
+
export declare class SdkAgent extends Agent {
|
|
55
|
+
/** 全局 Provider 实例 */
|
|
56
|
+
readonly provider: Provider;
|
|
57
|
+
/** Agent 原始定义 */
|
|
58
|
+
readonly definition: AgentDefinition;
|
|
59
|
+
/** Agent 权限 */
|
|
60
|
+
readonly permissions?: AgentPermissions;
|
|
61
|
+
/** Capabilities 全局注册表,用于运行时刷新工具列表 */
|
|
62
|
+
readonly caps?: Capabilities;
|
|
63
|
+
/** 已注册的插件列表 */
|
|
64
|
+
private _plugins;
|
|
65
|
+
constructor(params: {
|
|
66
|
+
provider: Provider;
|
|
67
|
+
definition: AgentDefinition;
|
|
68
|
+
model: ChatCompletionModel;
|
|
69
|
+
model_config?: Record<string, unknown>;
|
|
70
|
+
messages?: AgentNS.Message[];
|
|
71
|
+
tools?: Tool[];
|
|
72
|
+
permissions?: AgentPermissions;
|
|
73
|
+
caps?: Capabilities;
|
|
74
|
+
rag?: any;
|
|
75
|
+
allowJsonParseError?: boolean;
|
|
76
|
+
});
|
|
77
|
+
/**
|
|
78
|
+
* 注册一个插件。
|
|
79
|
+
* 可以在 init() 之前或之后调用,但 beforeSend/afterSend 钩子仅在
|
|
80
|
+
* init() 之后发送消息时生效。
|
|
81
|
+
*/
|
|
82
|
+
use(plugin: AgentPlugin): void;
|
|
83
|
+
/**
|
|
84
|
+
* 初始化所有已注册插件。
|
|
85
|
+
* SdkAgent 本身没有异步初始化需求——模型、工具、消息在构造时已就绪。
|
|
86
|
+
* init() 的存在完全是为了给插件一个执行异步初始化的机会。
|
|
87
|
+
*
|
|
88
|
+
* 如果不使用任何插件,可以不调 init()。
|
|
89
|
+
*/
|
|
90
|
+
init(): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* 发送消息,在前后执行插件钩子。
|
|
93
|
+
*
|
|
94
|
+
* 流程:
|
|
95
|
+
* 1. onBeforeSend — send 外部准备(刷新工具等)
|
|
96
|
+
* 2. super.send() — 委托 Core Agent,内含内循环及其钩子
|
|
97
|
+
* 3. onAfterSend — send 外部后处理(保存草稿、迁移等)
|
|
98
|
+
*/
|
|
99
|
+
send(content: string): Promise<AgentNS.Message[]>;
|
|
100
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Agent } from "@ai-zen/agents-core";
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// SdkAgent
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
/**
|
|
6
|
+
* SDK Agent — 继承 Core Agent,携带 SDK 层额外信息。
|
|
7
|
+
*
|
|
8
|
+
* Core Agent 不做复杂逻辑(不感知权限、不感知文件系统),
|
|
9
|
+
* SdkAgent 在 Core Agent 基础上增加了 SDK 层需要的元数据,
|
|
10
|
+
* 并通过 provider 字段访问全局上下文。
|
|
11
|
+
*
|
|
12
|
+
* 携带:
|
|
13
|
+
* - provider:全局 Provider 实例
|
|
14
|
+
* - definition:Agent 原始定义
|
|
15
|
+
* - permissions:Agent 权限,供 call_skill_sub_agent 等回调读取
|
|
16
|
+
* - caps:Capabilities 全局注册表,供 refreshTools 刷新工具列表
|
|
17
|
+
*
|
|
18
|
+
* 插件能力:
|
|
19
|
+
* - use(plugin):注册插件
|
|
20
|
+
* - init():初始化所有已注册插件
|
|
21
|
+
* - send():重写,在前后执行插件钩子
|
|
22
|
+
*/
|
|
23
|
+
export class SdkAgent extends Agent {
|
|
24
|
+
/** 全局 Provider 实例 */
|
|
25
|
+
provider;
|
|
26
|
+
/** Agent 原始定义 */
|
|
27
|
+
definition;
|
|
28
|
+
/** Agent 权限 */
|
|
29
|
+
permissions;
|
|
30
|
+
/** Capabilities 全局注册表,用于运行时刷新工具列表 */
|
|
31
|
+
caps;
|
|
32
|
+
/** 已注册的插件列表 */
|
|
33
|
+
_plugins = [];
|
|
34
|
+
constructor(params) {
|
|
35
|
+
super({
|
|
36
|
+
model: params.model,
|
|
37
|
+
model_config: params.model_config,
|
|
38
|
+
messages: params.messages,
|
|
39
|
+
tools: params.tools,
|
|
40
|
+
rag: params.rag,
|
|
41
|
+
allowJsonParseError: params.allowJsonParseError,
|
|
42
|
+
});
|
|
43
|
+
this.provider = params.provider;
|
|
44
|
+
this.definition = params.definition;
|
|
45
|
+
this.permissions = params.permissions;
|
|
46
|
+
this.caps = params.caps;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 注册一个插件。
|
|
50
|
+
* 可以在 init() 之前或之后调用,但 beforeSend/afterSend 钩子仅在
|
|
51
|
+
* init() 之后发送消息时生效。
|
|
52
|
+
*/
|
|
53
|
+
use(plugin) {
|
|
54
|
+
this._plugins.push(plugin);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 初始化所有已注册插件。
|
|
58
|
+
* SdkAgent 本身没有异步初始化需求——模型、工具、消息在构造时已就绪。
|
|
59
|
+
* init() 的存在完全是为了给插件一个执行异步初始化的机会。
|
|
60
|
+
*
|
|
61
|
+
* 如果不使用任何插件,可以不调 init()。
|
|
62
|
+
*/
|
|
63
|
+
async init() {
|
|
64
|
+
for (const plugin of this._plugins) {
|
|
65
|
+
await plugin.onInit?.();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 发送消息,在前后执行插件钩子。
|
|
70
|
+
*
|
|
71
|
+
* 流程:
|
|
72
|
+
* 1. onBeforeSend — send 外部准备(刷新工具等)
|
|
73
|
+
* 2. super.send() — 委托 Core Agent,内含内循环及其钩子
|
|
74
|
+
* 3. onAfterSend — send 外部后处理(保存草稿、迁移等)
|
|
75
|
+
*/
|
|
76
|
+
async send(content) {
|
|
77
|
+
const ctx = {
|
|
78
|
+
agent: this,
|
|
79
|
+
content,
|
|
80
|
+
messages: this.messages,
|
|
81
|
+
};
|
|
82
|
+
for (const plugin of this._plugins) {
|
|
83
|
+
await plugin.onBeforeSend?.(ctx);
|
|
84
|
+
}
|
|
85
|
+
this.onInnerLoopStart = async () => {
|
|
86
|
+
for (const plugin of this._plugins) {
|
|
87
|
+
await plugin.onInnerLoopStart?.(ctx);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
this.onInnerLoopEnd = async () => {
|
|
91
|
+
for (const plugin of this._plugins) {
|
|
92
|
+
await plugin.onInnerLoopEnd?.(ctx);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
const messages = await super.send(content);
|
|
96
|
+
this.onInnerLoopStart = undefined;
|
|
97
|
+
this.onInnerLoopEnd = undefined;
|
|
98
|
+
for (const plugin of this._plugins) {
|
|
99
|
+
await plugin.onAfterSend?.(ctx);
|
|
100
|
+
}
|
|
101
|
+
return messages;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { AgentNS } from "@ai-zen/agents-core";
|
|
2
|
+
import type { AgentDefinition } from "../types/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* 任务迁移服务。
|
|
5
|
+
*/
|
|
6
|
+
export declare class TaskMigrationService {
|
|
7
|
+
static readonly HANDOFF_SECTIONS: {
|
|
8
|
+
readonly breakpoint: "## 💬 对话断点";
|
|
9
|
+
readonly completed: "## ✅ 已完成的任务";
|
|
10
|
+
readonly pending: "## 📋 未完成的任务";
|
|
11
|
+
readonly memory: "## 🧠 重要记忆";
|
|
12
|
+
readonly files: "## 📁 文件索引";
|
|
13
|
+
readonly instructions: "## 🔔 接手指令";
|
|
14
|
+
};
|
|
15
|
+
static shouldMigrate(promptTokens: number, maxTokens: number): boolean;
|
|
16
|
+
static createPrompt(): string;
|
|
17
|
+
static createAgentDefinition(options?: {
|
|
18
|
+
modelId?: string;
|
|
19
|
+
}): AgentDefinition;
|
|
20
|
+
static createPostMessages(handoffDoc: string): AgentNS.Message[];
|
|
21
|
+
}
|
|
22
|
+
export interface BuildMigrationAgentOptions {
|
|
23
|
+
modelId?: string;
|
|
24
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { AgentNS } from "@ai-zen/agents-core";
|
|
2
|
+
/**
|
|
3
|
+
* 任务迁移服务。
|
|
4
|
+
*/
|
|
5
|
+
export class TaskMigrationService {
|
|
6
|
+
static HANDOFF_SECTIONS = {
|
|
7
|
+
breakpoint: "## 💬 对话断点",
|
|
8
|
+
completed: "## ✅ 已完成的任务",
|
|
9
|
+
pending: "## 📋 未完成的任务",
|
|
10
|
+
memory: "## 🧠 重要记忆",
|
|
11
|
+
files: "## 📁 文件索引",
|
|
12
|
+
instructions: "## 🔔 接手指令",
|
|
13
|
+
};
|
|
14
|
+
static shouldMigrate(promptTokens, maxTokens) {
|
|
15
|
+
return promptTokens > maxTokens;
|
|
16
|
+
}
|
|
17
|
+
static createPrompt() {
|
|
18
|
+
return [
|
|
19
|
+
"你是一个任务交接助手。请根据以下对话历史,生成一份结构化交接文档。",
|
|
20
|
+
"",
|
|
21
|
+
`文档必须包含以下章节:`,
|
|
22
|
+
`${TaskMigrationService.HANDOFF_SECTIONS.breakpoint}`,
|
|
23
|
+
`- 用户最后说了什么,AI 最后回复了什么`,
|
|
24
|
+
"",
|
|
25
|
+
`${TaskMigrationService.HANDOFF_SECTIONS.completed}`,
|
|
26
|
+
`- 列出已完成的每项任务及其关键产出`,
|
|
27
|
+
"",
|
|
28
|
+
`${TaskMigrationService.HANDOFF_SECTIONS.pending}`,
|
|
29
|
+
`- 按优先级列出未完成的任务、当前进度、下一步`,
|
|
30
|
+
"",
|
|
31
|
+
`${TaskMigrationService.HANDOFF_SECTIONS.memory}`,
|
|
32
|
+
`- 需要新 Agent 记住的关键信息(设计决策、偏好、约定等)`,
|
|
33
|
+
"",
|
|
34
|
+
`${TaskMigrationService.HANDOFF_SECTIONS.files}`,
|
|
35
|
+
`- 涉及的文件及其用途`,
|
|
36
|
+
"",
|
|
37
|
+
`${TaskMigrationService.HANDOFF_SECTIONS.instructions}`,
|
|
38
|
+
`- 接手后建议的操作步骤`,
|
|
39
|
+
"",
|
|
40
|
+
"只输出交接文档本身,不要加额外说明。",
|
|
41
|
+
].join("\n");
|
|
42
|
+
}
|
|
43
|
+
static createAgentDefinition(options = {}) {
|
|
44
|
+
const now = new Date().toISOString();
|
|
45
|
+
return {
|
|
46
|
+
id: "task-migration",
|
|
47
|
+
name: "任务交接助手",
|
|
48
|
+
description: "专用迁移 Agent:分析对话历史并生成结构化交接文档",
|
|
49
|
+
messages: [
|
|
50
|
+
{ role: AgentNS.Role.System, content: TaskMigrationService.createPrompt() },
|
|
51
|
+
],
|
|
52
|
+
modelId: options.modelId,
|
|
53
|
+
createdAt: now,
|
|
54
|
+
updatedAt: now,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
static createPostMessages(handoffDoc) {
|
|
58
|
+
const content = [
|
|
59
|
+
"这是上一轮对话的任务交接文档。请先阅读交接文档,理解上下文后再继续协助用户完成任务。",
|
|
60
|
+
"",
|
|
61
|
+
"---",
|
|
62
|
+
"",
|
|
63
|
+
handoffDoc,
|
|
64
|
+
"",
|
|
65
|
+
"---",
|
|
66
|
+
"",
|
|
67
|
+
"请确认你已理解以上内容,然后询问用户接下来需要什么帮助。",
|
|
68
|
+
].join("\n");
|
|
69
|
+
return [{ role: AgentNS.Role.User, content }];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SdkAgent } from "./SdkAgent.js";
|
|
2
|
+
import type { Provider } from "./Provider.js";
|
|
3
|
+
/**
|
|
4
|
+
* 从磁盘创建 Agent(同步)。
|
|
5
|
+
*
|
|
6
|
+
* 内部创建 Capabilities 实例执行全局发现,读取 Agent 定义,
|
|
7
|
+
* 过滤并实例化工具,产出 SdkAgent。
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* const agent = createAgent(runtime, "my-agent");
|
|
11
|
+
* // agent 是 SdkAgent,可直接注册插件、发送消息
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare function createAgent(provider: Provider, agentId: string): SdkAgent;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { AgentRepository } from "../crud/AgentRepository.js";
|
|
2
|
+
import { Capabilities } from "../capabilities/Capabilities.js";
|
|
3
|
+
import { SdkAgent } from "./SdkAgent.js";
|
|
4
|
+
import { createModel } from "./createModel.js";
|
|
5
|
+
/**
|
|
6
|
+
* 从磁盘创建 Agent(同步)。
|
|
7
|
+
*
|
|
8
|
+
* 内部创建 Capabilities 实例执行全局发现,读取 Agent 定义,
|
|
9
|
+
* 过滤并实例化工具,产出 SdkAgent。
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* const agent = createAgent(runtime, "my-agent");
|
|
13
|
+
* // agent 是 SdkAgent,可直接注册插件、发送消息
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export function createAgent(provider, agentId) {
|
|
17
|
+
const caps = new Capabilities(provider);
|
|
18
|
+
const definition = new AgentRepository(provider.agentsDir).read(agentId);
|
|
19
|
+
if (!definition)
|
|
20
|
+
throw new Error(`Agent "${agentId}" 不存在`);
|
|
21
|
+
const modelId = definition.modelId ?? provider.config.defaultModel;
|
|
22
|
+
if (!modelId)
|
|
23
|
+
throw new Error("未指定模型且无默认模型");
|
|
24
|
+
const model = createModel(provider.config, modelId);
|
|
25
|
+
const tools = caps.buildTools(definition.permissions ?? {}, {
|
|
26
|
+
exclude: {
|
|
27
|
+
subagents: definition.function?.name
|
|
28
|
+
? [definition.function.name]
|
|
29
|
+
: undefined,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
return new SdkAgent({
|
|
33
|
+
provider,
|
|
34
|
+
definition,
|
|
35
|
+
model,
|
|
36
|
+
messages: definition.messages,
|
|
37
|
+
tools,
|
|
38
|
+
permissions: definition.permissions,
|
|
39
|
+
caps,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ChatCompletionModel } from "@ai-zen/agents-core";
|
|
2
|
+
import type { AppConfig } from "../types/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* 模型工厂 — 根据 modelId 和 config 构建 Core ChatCompletionModel。
|
|
5
|
+
*
|
|
6
|
+
* 属于独立的 runtime 子层,不依赖 capabilities/ 或其他模块。
|
|
7
|
+
* 通过 Provider 实例获取 AppConfig 后调用。
|
|
8
|
+
*
|
|
9
|
+
* 改为同步版本,使用 endpoint.chatCompletionSync() 避免不必要的 async。
|
|
10
|
+
*/
|
|
11
|
+
export declare function createModel(config: AppConfig, modelId: string): ChatCompletionModel;
|