@amical/aiagent-sdk 0.1.0 → 0.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.
- package/dist/index.d.mts +476 -0
- package/dist/index.d.ts +476 -0
- package/dist/index.js +968 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +951 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +7 -1
- package/.agents/skills/Viral_Writer/SKILL.md +0 -239
- package/.cline_memory.json +0 -58
- package/.node-version +0 -1
- package/Agents.md +0 -95
- package/src/agent/Agent.ts +0 -566
- package/src/agent/index.ts +0 -1
- package/src/index.ts +0 -15
- package/src/llm/NodeApiHandler.ts +0 -117
- package/src/llm/WebApiHandler.ts +0 -118
- package/src/llm/index.ts +0 -47
- package/src/mcp/NodeMcpManager.ts +0 -158
- package/src/mcp/WebMcpManager.ts +0 -30
- package/src/mcp/index.ts +0 -56
- package/src/memory/NodeMemoryManager.ts +0 -82
- package/src/memory/WebMemoryManager.ts +0 -89
- package/src/memory/index.ts +0 -36
- package/src/parser/diff.ts +0 -855
- package/src/parser/index.ts +0 -111
- package/src/parser/parse-assistant-message.ts +0 -240
- package/src/prompt/NodePromptManager.ts +0 -276
- package/src/prompt/WebPromptManager.ts +0 -277
- package/src/prompt/index.ts +0 -28
- package/src/runtime/nodeRuntime.ts +0 -112
- package/src/runtime/web.test.ts +0 -95
- package/src/runtime/webRuntime.ts +0 -119
- package/src/runtime.ts +0 -46
- package/src/shared/tools.ts +0 -270
- package/test-node-agent.ts +0 -113
- package/test-runtime.mjs +0 -36
- package/test.md +0 -33
- package/tsconfig.json +0 -28
- package/tsup.config.ts +0 -21
- package/uapi.ts +0 -3
- package/webFetch.ts +0 -49
- package/webSearch.ts +0 -19
package/src/agent/Agent.ts
DELETED
|
@@ -1,566 +0,0 @@
|
|
|
1
|
-
import { IAgentRuntime } from '../runtime';
|
|
2
|
-
import { IApiHandler, ApiStreamChunk } from '../llm/index';
|
|
3
|
-
import { IPromptManager } from '../prompt/index';
|
|
4
|
-
import { IMemoryManager, AgentMessage } from '../memory/index';
|
|
5
|
-
import { IMcpManager } from '../mcp/index';
|
|
6
|
-
import { parseAssistantMessageV2, ToolUse, toolParamNames } from '../parser/index';
|
|
7
|
-
import { AgentDefaultTool, setDynamicToolUseNames, TOOL_RESULT_BLOCK } from '../shared/tools';
|
|
8
|
-
import matter from 'gray-matter';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Agent 初始化配置依赖
|
|
12
|
-
*/
|
|
13
|
-
export interface AgentOptions {
|
|
14
|
-
runtime: IAgentRuntime;
|
|
15
|
-
api: IApiHandler;
|
|
16
|
-
promptManager: IPromptManager;
|
|
17
|
-
memory: IMemoryManager;
|
|
18
|
-
mcp?: IMcpManager;
|
|
19
|
-
customTools?: any[];
|
|
20
|
-
allowDefaultTools?: string[];
|
|
21
|
-
customInstructions?: string;
|
|
22
|
-
/** 最大循环次数,默认150 */
|
|
23
|
-
maxLoopCount?: number;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Agent 状态
|
|
28
|
-
*/
|
|
29
|
-
export enum AgentState {
|
|
30
|
-
IDLE = 'idle',
|
|
31
|
-
THINKING = 'thinking',
|
|
32
|
-
WAITING_FOR_USER = 'waiting_for_user',
|
|
33
|
-
FINISHED = 'finished',
|
|
34
|
-
ERROR = 'error',
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* ClineAgent 核心逻辑循环状态机
|
|
39
|
-
* 它独立于 VS Code 或 Node 环境,纯粹通过依赖注入的方式执行工作流。
|
|
40
|
-
*/
|
|
41
|
-
export class ClineAgent {
|
|
42
|
-
private runtime: IAgentRuntime;
|
|
43
|
-
private api: IApiHandler;
|
|
44
|
-
private promptManager: IPromptManager;
|
|
45
|
-
private memory: IMemoryManager;
|
|
46
|
-
private mcp?: IMcpManager;
|
|
47
|
-
|
|
48
|
-
public state: AgentState = AgentState.IDLE;
|
|
49
|
-
public allToolParamNames: string[] = [...toolParamNames];
|
|
50
|
-
/** 循环计数器 */
|
|
51
|
-
private loopCount: number = 0;
|
|
52
|
-
/** 最大循环次数 */
|
|
53
|
-
private maxLoopCount: number;
|
|
54
|
-
|
|
55
|
-
skillDir: string;
|
|
56
|
-
customTools: any[];
|
|
57
|
-
customToolNames: Set<string>;
|
|
58
|
-
allowDefaultTools?: string[];
|
|
59
|
-
customInstructions?: string;
|
|
60
|
-
|
|
61
|
-
sysPrompt?: string;
|
|
62
|
-
noUseTollSysPrompt?: string;
|
|
63
|
-
|
|
64
|
-
// 简单的事件回调系统,供宿主环境注册以监听 Agent 内部事件
|
|
65
|
-
public onStateChange?: (state: AgentState) => void;
|
|
66
|
-
public onStreamChunk?: (chunk: ApiStreamChunk) => void;
|
|
67
|
-
public onToolCall?: (tool: ToolUse) => Promise<any | void>;
|
|
68
|
-
|
|
69
|
-
constructor(options: AgentOptions) {
|
|
70
|
-
this.runtime = options.runtime;
|
|
71
|
-
this.api = options.api;
|
|
72
|
-
this.promptManager = options.promptManager;
|
|
73
|
-
this.memory = options.memory;
|
|
74
|
-
this.mcp = options.mcp;
|
|
75
|
-
this.maxLoopCount = options.maxLoopCount ?? 150;
|
|
76
|
-
this.customTools = options.customTools || [];
|
|
77
|
-
this.allowDefaultTools = options.allowDefaultTools;
|
|
78
|
-
if (this.allowDefaultTools && !this.allowDefaultTools.includes(AgentDefaultTool.ATTEMPT)) {
|
|
79
|
-
this.allowDefaultTools.push(AgentDefaultTool.ATTEMPT);
|
|
80
|
-
}
|
|
81
|
-
this.customInstructions = options.customInstructions;
|
|
82
|
-
if (this.customTools.length > 0) {
|
|
83
|
-
const dynamicToolNames = new Set<string>();
|
|
84
|
-
const customToolParams = new Set<string>();
|
|
85
|
-
for (const customTool of this.customTools) {
|
|
86
|
-
dynamicToolNames.add(customTool.name);
|
|
87
|
-
if (customTool.params) {
|
|
88
|
-
for (const key in customTool.params) {
|
|
89
|
-
if (!Object.hasOwn(customTool.params, key)) continue;
|
|
90
|
-
customToolParams.add(key);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// 检测和内置工具的重复,抛出错误
|
|
96
|
-
const defaultToolNames = Object.values(AgentDefaultTool);
|
|
97
|
-
const overlappingTools = Array.from(dynamicToolNames).filter((name: any) => defaultToolNames.includes(name));
|
|
98
|
-
if (overlappingTools.length > 0) {
|
|
99
|
-
throw new Error(`Custom tools and default tools have overlapping names: ${overlappingTools.join(', ')}`);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (dynamicToolNames.size > 0) {
|
|
103
|
-
setDynamicToolUseNames('customUser', Array.from(dynamicToolNames));
|
|
104
|
-
}
|
|
105
|
-
if (customToolParams.size > 0) {
|
|
106
|
-
this.allToolParamNames = Array.from(new Set([...this.allToolParamNames, ...Array.from(customToolParams)]));
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
this.customToolNames = new Set<string>(this.customTools.map((t: any) => t.name));
|
|
110
|
-
this.skillDir = this.runtime.path.join(this.runtime.env.cwd(), '.agent/skills');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
private setState(newState: AgentState) {
|
|
114
|
-
this.state = newState;
|
|
115
|
-
if (this.onStateChange) {
|
|
116
|
-
this.onStateChange(newState);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* 获取当前 Agent 的系统提示词
|
|
122
|
-
* 如果 force 为 true,则强制重新获取系统提示词
|
|
123
|
-
*/
|
|
124
|
-
public async getPrompt(force = false) {
|
|
125
|
-
if (!force && this.sysPrompt && this.noUseTollSysPrompt) {
|
|
126
|
-
return { sysPrompt: this.sysPrompt, noUseTollSysPrompt: this.noUseTollSysPrompt };
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const context = {
|
|
130
|
-
cwd: this.runtime.env.cwd(),
|
|
131
|
-
os: this.runtime.env.osInfo(),
|
|
132
|
-
shell: this.runtime.env.shell(),
|
|
133
|
-
skills: await this.getSkills(),
|
|
134
|
-
customTools: this.customTools,
|
|
135
|
-
allowDefaultTools: this.allowDefaultTools,
|
|
136
|
-
customInstructions: this.customInstructions,
|
|
137
|
-
};
|
|
138
|
-
// 1. 获取动态系统提示词
|
|
139
|
-
this.sysPrompt = await this.promptManager.getSystemPrompt(context);
|
|
140
|
-
this.noUseTollSysPrompt = this.promptManager.getNoUseToolInstructions(context);
|
|
141
|
-
|
|
142
|
-
return { sysPrompt: this.sysPrompt, noUseTollSysPrompt: this.noUseTollSysPrompt };
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* 启动一个新的任务
|
|
147
|
-
*/
|
|
148
|
-
public async startTask(taskDescription: AgentMessage['content']): Promise<void> {
|
|
149
|
-
this.setState(AgentState.THINKING);
|
|
150
|
-
try {
|
|
151
|
-
await this.memory.clear();
|
|
152
|
-
// 重置循环计数器
|
|
153
|
-
this.loopCount = 0;
|
|
154
|
-
await this.memory.appendMessage({
|
|
155
|
-
role: 'user',
|
|
156
|
-
content: taskDescription,
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
await this.loop();
|
|
160
|
-
} catch (err) {
|
|
161
|
-
this.setState(AgentState.ERROR);
|
|
162
|
-
throw err;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
private async getSkills() {
|
|
167
|
-
const skillDir = this.skillDir;
|
|
168
|
-
const skillList: { name: string; description: string }[] = [];
|
|
169
|
-
if (!this.runtime.fs.fileExists(skillDir)) {
|
|
170
|
-
return skillList;
|
|
171
|
-
}
|
|
172
|
-
const files = await this.runtime.fs.readDir(skillDir);
|
|
173
|
-
const skills = files.filter((item) => item.isDirectory()).map((item) => item.name);
|
|
174
|
-
|
|
175
|
-
if (skills.length === 0) {
|
|
176
|
-
return [];
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
for (const skill of skills) {
|
|
180
|
-
try {
|
|
181
|
-
const skillPath = this.runtime.path.join(skillDir, skill);
|
|
182
|
-
const files = await this.runtime.fs.readDir(skillPath);
|
|
183
|
-
const skillFile = files.map((item) => item.isFile()).find((f) => f.toLowerCase() === 'skill.md');
|
|
184
|
-
if (skillFile) {
|
|
185
|
-
const mdPath = this.runtime.path.join(skillPath, skillFile);
|
|
186
|
-
const mdContent = await this.runtime.fs.readFile(mdPath);
|
|
187
|
-
const parsed = matter(mdContent);
|
|
188
|
-
if (parsed.data && parsed.data.description) {
|
|
189
|
-
let description = String(parsed.data.description).trim();
|
|
190
|
-
description = description.replace(/\s+/g, ' ').trim();
|
|
191
|
-
if (description) {
|
|
192
|
-
skillList.push({
|
|
193
|
-
name: skill,
|
|
194
|
-
description: description,
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
} catch (e) {
|
|
200
|
-
// SKILL.md might not exist or be invalid, ignore
|
|
201
|
-
return skillList;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* 核心任务循环
|
|
208
|
-
*/
|
|
209
|
-
private async loop(): Promise<void> {
|
|
210
|
-
const {sysPrompt, noUseTollSysPrompt} = await this.getPrompt(true);
|
|
211
|
-
const {customTools, customToolNames} = this;
|
|
212
|
-
while (this.state === AgentState.THINKING) {
|
|
213
|
-
this.loopCount++;
|
|
214
|
-
// 检查是否超过最大循环次数
|
|
215
|
-
if (this.loopCount > this.maxLoopCount) {
|
|
216
|
-
await this.memory.appendMessage({
|
|
217
|
-
role: 'user',
|
|
218
|
-
content: `[ERROR] You have exceeded the maximum loop limit of ${this.maxLoopCount} times. Please immediately call the agent_attempt_completion tool to return the final result. If you have pending user-specified tools to execute, you may run them first before calling agent_attempt_completion.`,
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
// 1.历史记忆
|
|
222
|
-
const history = await this.memory.getHistory();
|
|
223
|
-
|
|
224
|
-
// 2. 调用 API
|
|
225
|
-
const stream = this.api.createMessage(sysPrompt, history);
|
|
226
|
-
let fullResponseText = '';
|
|
227
|
-
|
|
228
|
-
// 3. 处理流式输出
|
|
229
|
-
for await (const chunk of stream) {
|
|
230
|
-
if (chunk.type === 'text' && chunk.text) {
|
|
231
|
-
fullResponseText += chunk.text;
|
|
232
|
-
}
|
|
233
|
-
if (this.onStreamChunk) {
|
|
234
|
-
this.onStreamChunk(chunk);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// 4. 将助手的完整回复记入 Memory
|
|
239
|
-
await this.memory.appendMessage({
|
|
240
|
-
role: 'assistant',
|
|
241
|
-
content: fullResponseText,
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
// 5. 解析回复中的工具调用
|
|
245
|
-
const parsedBlocks = parseAssistantMessageV2(fullResponseText, this.allToolParamNames);
|
|
246
|
-
const toolUses = parsedBlocks.filter((b) => b.type === 'tool_use') as ToolUse[];
|
|
247
|
-
|
|
248
|
-
if (toolUses.length === 0) {
|
|
249
|
-
// 如果没有工具调用,错误
|
|
250
|
-
await this.memory.appendMessage({
|
|
251
|
-
role: 'user',
|
|
252
|
-
content: noUseTollSysPrompt,
|
|
253
|
-
});
|
|
254
|
-
} else {
|
|
255
|
-
// 6. 顺序执行工具(为简化模型,暂时使用 await 顺序执行)
|
|
256
|
-
const toolResults = [];
|
|
257
|
-
for (const tool of toolUses) {
|
|
258
|
-
if (this.onToolCall) {
|
|
259
|
-
// 如果宿主接管了工具执行或需要拦截请求权限
|
|
260
|
-
const hookResult = await this.onToolCall(tool);
|
|
261
|
-
if (hookResult) {
|
|
262
|
-
toolResults.push({ tool, result: hookResult });
|
|
263
|
-
continue;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// 外部工具调用
|
|
268
|
-
if (customToolNames.has(tool.name)) {
|
|
269
|
-
const customTool = customTools.find((t: any) => t.name === tool.name);
|
|
270
|
-
if (customTool.onCall) {
|
|
271
|
-
try {
|
|
272
|
-
const res = await customTool.onCall(tool.params);
|
|
273
|
-
toolResults.push({ tool, result: res });
|
|
274
|
-
} catch (error: any) {
|
|
275
|
-
toolResults.push({ tool, result: `Error: ${error.message}` });
|
|
276
|
-
}
|
|
277
|
-
} else {
|
|
278
|
-
toolResults.push({ tool, result: `Error: Custom tool "${tool.name}" does not have an onCall method.` });
|
|
279
|
-
}
|
|
280
|
-
continue;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// 内置逻辑分发(可由 IAgentRuntime 支撑的核心工具)
|
|
284
|
-
if (tool.name === AgentDefaultTool.ATTEMPT) {
|
|
285
|
-
toolResults.push({ tool, result: 'Task marked as completed.' });
|
|
286
|
-
this.setState(AgentState.FINISHED);
|
|
287
|
-
break; // 中断循环
|
|
288
|
-
} else if (tool.name === AgentDefaultTool.FILE_READ) {
|
|
289
|
-
const path = tool.params.path;
|
|
290
|
-
if (path) {
|
|
291
|
-
try {
|
|
292
|
-
const content = await this.runtime.fs.readFile(path);
|
|
293
|
-
toolResults.push({ tool, result: content });
|
|
294
|
-
} catch (error: any) {
|
|
295
|
-
toolResults.push({ tool, result: `Error: ${error.message}` });
|
|
296
|
-
}
|
|
297
|
-
} else {
|
|
298
|
-
toolResults.push({ tool, result: 'Error: No path provided.' });
|
|
299
|
-
}
|
|
300
|
-
} else if (tool.name === AgentDefaultTool.BASH) {
|
|
301
|
-
const cmd = tool.params.command;
|
|
302
|
-
if (cmd) {
|
|
303
|
-
const result = await this.runtime.terminal.execCommand(cmd);
|
|
304
|
-
toolResults.push({ tool, result: `STDOUT: ${result.stdout}\nSTDERR: ${result.stderr}` });
|
|
305
|
-
}
|
|
306
|
-
} else if (tool.name === AgentDefaultTool.FILE_NEW) {
|
|
307
|
-
const path = tool.params.path;
|
|
308
|
-
const content = tool.params.content;
|
|
309
|
-
if (path && content !== undefined) {
|
|
310
|
-
try {
|
|
311
|
-
await this.runtime.fs.writeFile(path, content);
|
|
312
|
-
toolResults.push({ tool, result: `Successfully wrote to ${path}` });
|
|
313
|
-
} catch (error: any) {
|
|
314
|
-
toolResults.push({ tool, result: `Error: ${error.message}` });
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
} else if (tool.name === AgentDefaultTool.LIST_FILES) {
|
|
318
|
-
const path = tool.params.path || this.runtime.env.cwd();
|
|
319
|
-
try {
|
|
320
|
-
const files = await this.runtime.fs.readDir(path);
|
|
321
|
-
// 输出是 文件还是目录
|
|
322
|
-
toolResults.push({ tool, result: files.map((f) => f.name + (f.isDirectory() ? '/' : '')).join('\n') });
|
|
323
|
-
} catch (e: any) {
|
|
324
|
-
toolResults.push({ tool, result: `Error: ${e.message}` });
|
|
325
|
-
}
|
|
326
|
-
} else if (tool.name === AgentDefaultTool.FILE_EDIT) {
|
|
327
|
-
const path = tool.params.path;
|
|
328
|
-
const diff = tool.params.diff;
|
|
329
|
-
if (path && diff) {
|
|
330
|
-
try {
|
|
331
|
-
const content = await this.runtime.fs.readFile(path);
|
|
332
|
-
const newContent = this.applyDiff(content, diff);
|
|
333
|
-
await this.runtime.fs.writeFile(path, newContent);
|
|
334
|
-
toolResults.push({ tool, result: `Successfully replaced content in ${path}` });
|
|
335
|
-
} catch (e: any) {
|
|
336
|
-
toolResults.push({ tool, result: `Error: ${e.message}` });
|
|
337
|
-
}
|
|
338
|
-
} else {
|
|
339
|
-
toolResults.push({ tool, result: 'Error: Missing path or diff.' });
|
|
340
|
-
}
|
|
341
|
-
} else if (tool.name === AgentDefaultTool.FILE_DELETE) {
|
|
342
|
-
const path = tool.params.path;
|
|
343
|
-
if (path) {
|
|
344
|
-
try {
|
|
345
|
-
await this.runtime.fs.deleteFile(path);
|
|
346
|
-
toolResults.push({ tool, result: `Successfully deleted ${path}` });
|
|
347
|
-
} catch (e: any) {
|
|
348
|
-
toolResults.push({ tool, result: `Error: ${e.message}` });
|
|
349
|
-
}
|
|
350
|
-
} else {
|
|
351
|
-
toolResults.push({ tool, result: 'Error: Missing path.' });
|
|
352
|
-
}
|
|
353
|
-
} else if (tool.name === AgentDefaultTool.DIR_DELETE) {
|
|
354
|
-
const path = tool.params.path;
|
|
355
|
-
if (path) {
|
|
356
|
-
try {
|
|
357
|
-
await this.runtime.fs.deleteDirectory(path);
|
|
358
|
-
toolResults.push({ tool, result: `Successfully deleted ${path}` });
|
|
359
|
-
} catch (e: any) {
|
|
360
|
-
toolResults.push({ tool, result: `Error: ${e.message}` });
|
|
361
|
-
}
|
|
362
|
-
} else {
|
|
363
|
-
toolResults.push({ tool, result: 'Error: Missing path.' });
|
|
364
|
-
}
|
|
365
|
-
} else if (tool.name === AgentDefaultTool.ASK) {
|
|
366
|
-
const question = tool.params.question;
|
|
367
|
-
toolResults.push({ tool, result: `Asking user: ${question}` });
|
|
368
|
-
this.setState(AgentState.WAITING_FOR_USER);
|
|
369
|
-
// 遇到主动提问,立刻跳出循环,等待外部应用 resume()
|
|
370
|
-
break;
|
|
371
|
-
} else if (tool.name === AgentDefaultTool.SEARCH) {
|
|
372
|
-
const path = tool.params.path || this.runtime.env.cwd();
|
|
373
|
-
const regexStr = tool.params.regex;
|
|
374
|
-
const filePattern = tool.params.file_pattern;
|
|
375
|
-
if (regexStr && this.runtime.fs.searchFiles) {
|
|
376
|
-
try {
|
|
377
|
-
const result = await this.runtime.fs.searchFiles(path, regexStr, filePattern);
|
|
378
|
-
toolResults.push({ tool, result: `${result}` });
|
|
379
|
-
} catch (e: any) {
|
|
380
|
-
toolResults.push({ tool, result: `Error: ${e.message}` });
|
|
381
|
-
}
|
|
382
|
-
} else {
|
|
383
|
-
toolResults.push({ tool, result: `Error: searchFiles not supported by runtime or missing regex.` });
|
|
384
|
-
}
|
|
385
|
-
} else if (tool.name === AgentDefaultTool.MCP_USE) {
|
|
386
|
-
const serverName = tool.params.server_name;
|
|
387
|
-
const toolName = tool.params.tool_name;
|
|
388
|
-
const args = tool.params.arguments;
|
|
389
|
-
if (this.mcp && serverName && toolName) {
|
|
390
|
-
try {
|
|
391
|
-
let parsedArgs = args;
|
|
392
|
-
if (typeof args === 'string') {
|
|
393
|
-
parsedArgs = JSON.parse(args);
|
|
394
|
-
}
|
|
395
|
-
const result = await this.mcp.callTool(serverName, toolName, parsedArgs);
|
|
396
|
-
if (result.isError) {
|
|
397
|
-
toolResults.push({ tool, result: `Error executing MCP tool: ${JSON.stringify(result.content)}` });
|
|
398
|
-
} else {
|
|
399
|
-
toolResults.push({ tool, result: `${JSON.stringify(result.content)}` });
|
|
400
|
-
}
|
|
401
|
-
} catch (e: any) {
|
|
402
|
-
toolResults.push({ tool, result: `Error: ${e.message}` });
|
|
403
|
-
}
|
|
404
|
-
} else {
|
|
405
|
-
toolResults.push({ tool, result: `Error: MCP Manager not available or missing parameters.` });
|
|
406
|
-
}
|
|
407
|
-
} else if (tool.name === AgentDefaultTool.MCP_ACCESS) {
|
|
408
|
-
const serverName = tool.params.server_name;
|
|
409
|
-
const uri = tool.params.uri;
|
|
410
|
-
if (this.mcp && this.mcp.readResource && serverName && uri) {
|
|
411
|
-
try {
|
|
412
|
-
const result = await this.mcp.readResource(serverName, uri);
|
|
413
|
-
toolResults.push({ tool, result: `${JSON.stringify(result)}` });
|
|
414
|
-
} catch (e: any) {
|
|
415
|
-
toolResults.push({ tool, result: `Error: ${e.message}` });
|
|
416
|
-
}
|
|
417
|
-
} else {
|
|
418
|
-
toolResults.push({ tool, result: `Error: MCP Manager not available, readResource not implemented, or missing parameters.` });
|
|
419
|
-
}
|
|
420
|
-
} else if (tool.name === AgentDefaultTool.MCP_DOCS) {
|
|
421
|
-
// 提供一个简短的关于创建和使用 MCP 的静态文档,供 LLM 了解如何构建和提供 MCP
|
|
422
|
-
const docs = `MCP (Model Context Protocol) is used to connect to external tools/resources. To build one, typically you create an HTTP/stdio server responding to MCP defined JSON-RPC messages...`;
|
|
423
|
-
toolResults.push({ tool, result: docs });
|
|
424
|
-
} else if (tool.name === AgentDefaultTool.LIST_CODE_DEF) {
|
|
425
|
-
// 简单的 LIST_CODE_DEF 实现
|
|
426
|
-
const path = tool.params.path || this.runtime.env.cwd();
|
|
427
|
-
try {
|
|
428
|
-
const result = await this.runtime.fs.searchFiles?.(path, '^(class|function|const|let|var|export|interface|type)\\s+\\w+', '*.ts|*.js');
|
|
429
|
-
toolResults.push({ tool, result: `${result ? result : 'No definitions found or searchFiles not supported.'}` });
|
|
430
|
-
} catch (e: any) {
|
|
431
|
-
toolResults.push({ tool, result: `Error: ${e.message}` });
|
|
432
|
-
}
|
|
433
|
-
} else if (tool.name === AgentDefaultTool.WEB_FETCH) {
|
|
434
|
-
const url = tool.params.url;
|
|
435
|
-
if (url) {
|
|
436
|
-
try {
|
|
437
|
-
const res = await fetch(url);
|
|
438
|
-
const text = await res.text();
|
|
439
|
-
// 简单截断防止超长
|
|
440
|
-
toolResults.push({ tool, result: text.substring(0, 3000) });
|
|
441
|
-
} catch (e: any) {
|
|
442
|
-
toolResults.push({ tool, result: `Error: ${e.message}` });
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
} else if (tool.name === AgentDefaultTool.TODO) {
|
|
446
|
-
toolResults.push({ tool, result: `Focus chain updated successfully.` });
|
|
447
|
-
} else if (tool.name === AgentDefaultTool.PLAN_MODE || tool.name === AgentDefaultTool.ACT_MODE) {
|
|
448
|
-
toolResults.push({ tool, result: `Mode switch acknowledged.` });
|
|
449
|
-
} else if (tool.name === AgentDefaultTool.NEW_RULE) {
|
|
450
|
-
toolResults.push({ tool, result: `Rule recorded in current context.` });
|
|
451
|
-
} else if (tool.name === AgentDefaultTool.NEW_TASK) {
|
|
452
|
-
toolResults.push({ tool, result: `New task request received. Terminating current task.` });
|
|
453
|
-
this.setState(AgentState.FINISHED);
|
|
454
|
-
break;
|
|
455
|
-
} else if (tool.name === AgentDefaultTool.LOAD_SKILL) {
|
|
456
|
-
const skillName = (tool.params as any).skill_name || (tool.params as any).skill;
|
|
457
|
-
if (!skillName) {
|
|
458
|
-
toolResults.push({ tool, result: `"Error: Missing required parameter 'skill_name'. Please provide the name of the skill to activate."` });
|
|
459
|
-
} else {
|
|
460
|
-
const skillDir = this.skillDir;
|
|
461
|
-
// 按照约定从本地硬盘查找 skillDir/SKILL.md
|
|
462
|
-
const skillFilePath = this.runtime.path.join(skillDir, skillName, 'SKILL.md');
|
|
463
|
-
try {
|
|
464
|
-
const fileExists = await this.runtime.fs.fileExists(skillFilePath);
|
|
465
|
-
if (!fileExists) {
|
|
466
|
-
toolResults.push({ tool, result: `Error: Skill "${skillName}" not found at ${skillFilePath}.` });
|
|
467
|
-
} else {
|
|
468
|
-
const content = await this.runtime.fs.readFile(skillFilePath);
|
|
469
|
-
toolResults.push({
|
|
470
|
-
tool,
|
|
471
|
-
result: `# Skill "${skillName}" is now active\n\n${content}\n\n---\nIMPORTANT: The skill is now loaded. Do NOT call agent_load_skill again for this task. Simply follow the instructions above to complete the user's request.`,
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
} catch (err: any) {
|
|
475
|
-
toolResults.push({ tool, result: `Error loading skill "${skillName}": ${err.message}` });
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
} else if (tool.name === AgentDefaultTool.BROWSER || tool.name === AgentDefaultTool.WEB_SEARCH || tool.name === AgentDefaultTool.APPLY_PATCH) {
|
|
479
|
-
// 这些高级工具通常需要宿主环境的深度集成(如 Puppeteer、真实搜索引擎、Patch 引擎等)
|
|
480
|
-
// 如果宿主没有通过 onToolCall 拦截,默认返回提示信息
|
|
481
|
-
toolResults.push({ tool, result: `This tool requires specific host integration. If you see this, the host environment has not implemented it yet.` });
|
|
482
|
-
} else if (
|
|
483
|
-
tool.name === AgentDefaultTool.CONDENSE ||
|
|
484
|
-
tool.name === AgentDefaultTool.SUMMARIZE_TASK ||
|
|
485
|
-
tool.name === AgentDefaultTool.REPORT_BUG ||
|
|
486
|
-
tool.name === AgentDefaultTool.GENERATE_EXPLANATION
|
|
487
|
-
) {
|
|
488
|
-
// 这些主要起记录/输出作用的工具
|
|
489
|
-
toolResults.push({ tool, result: `Action logged successfully.` });
|
|
490
|
-
} else {
|
|
491
|
-
// 未知或新增工具兜底
|
|
492
|
-
toolResults.push({ tool, result: `Executed by host or unsupported.` });
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
// 7. 如果任务未完成,把执行结果告诉 LLM
|
|
497
|
-
if (this.state === AgentState.THINKING && toolResults.length > 0) {
|
|
498
|
-
const toolResultsContext = toolResults
|
|
499
|
-
.map((tr) => `<${TOOL_RESULT_BLOCK} tool_name="${tr.tool.name}">\n${typeof tr.result === 'string' ? tr.result.trim() : JSON.stringify(tr.result)}\n</${TOOL_RESULT_BLOCK}>`)
|
|
500
|
-
.join('\n\n');
|
|
501
|
-
// const lastResult = toolResults[toolResults.length - 1];
|
|
502
|
-
// if (lastResult && lastResult.tool.params.task_progress) {
|
|
503
|
-
// toolResultsContext += `\n<task_progress>\n${lastResult.tool.params.task_progress}\n</task_progress>\n`;
|
|
504
|
-
// }
|
|
505
|
-
if (this.onStreamChunk) {
|
|
506
|
-
this.onStreamChunk({ type: 'text', text: toolResultsContext });
|
|
507
|
-
}
|
|
508
|
-
await this.memory.appendMessage({
|
|
509
|
-
role: 'user',
|
|
510
|
-
content: toolResultsContext,
|
|
511
|
-
});
|
|
512
|
-
// 循环将继续,发送新的请求
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
/**
|
|
519
|
-
* 简单的 Diff 解析与替换辅助方法
|
|
520
|
-
*/
|
|
521
|
-
private applyDiff(content: string, diff: string): string {
|
|
522
|
-
const regex = /-------\s*SEARCH\r?\n(.*?)\r?\n=======\r?\n(.*?)\r?\n\+\+\+\+\+\+\+\s*REPLACE/gs;
|
|
523
|
-
let newContent = content;
|
|
524
|
-
let match;
|
|
525
|
-
let matchCount = 0;
|
|
526
|
-
while ((match = regex.exec(diff)) !== null) {
|
|
527
|
-
matchCount++;
|
|
528
|
-
const searchBlock = match[1];
|
|
529
|
-
const replaceBlock = match[2];
|
|
530
|
-
const idx = newContent.indexOf(searchBlock);
|
|
531
|
-
if (idx !== -1) {
|
|
532
|
-
newContent = newContent.substring(0, idx) + replaceBlock + newContent.substring(idx + searchBlock.length);
|
|
533
|
-
} else {
|
|
534
|
-
throw new Error(`Search block not found in file:\n${searchBlock.substring(0, 100)}...`);
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
if (matchCount === 0) {
|
|
538
|
-
throw new Error(`No valid SEARCH/REPLACE blocks found in the diff.`);
|
|
539
|
-
}
|
|
540
|
-
return newContent;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
/**
|
|
544
|
-
* 恢复被中断的循环(例如等待用户输入/确认后继续)
|
|
545
|
-
*/
|
|
546
|
-
public async resume(userInput?: string): Promise<void> {
|
|
547
|
-
if (userInput) {
|
|
548
|
-
await this.memory.appendMessage({
|
|
549
|
-
role: 'user',
|
|
550
|
-
content: userInput,
|
|
551
|
-
});
|
|
552
|
-
}
|
|
553
|
-
this.setState(AgentState.THINKING);
|
|
554
|
-
await this.loop();
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
public async appendHistory(history: AgentMessage[]) {
|
|
558
|
-
for (const message of history) {
|
|
559
|
-
await this.memory.appendMessage(message);
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
public async getHistory() {
|
|
564
|
-
return this.memory.getHistory();
|
|
565
|
-
}
|
|
566
|
-
}
|
package/src/agent/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./Agent";
|
package/src/index.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export const VERSION = '0.1.0';
|
|
2
|
-
|
|
3
|
-
globalThis.AI_SDK_LOG_WARNINGS = false;
|
|
4
|
-
|
|
5
|
-
export * from './runtime';
|
|
6
|
-
export * from './shared/tools';
|
|
7
|
-
export * from './parser';
|
|
8
|
-
export * from './prompt';
|
|
9
|
-
export * from './llm';
|
|
10
|
-
export * from './mcp';
|
|
11
|
-
export * from './agent';
|
|
12
|
-
export * from './memory';
|
|
13
|
-
|
|
14
|
-
export { createNodeAgent } from './runtime/nodeRuntime';
|
|
15
|
-
export { createWebAgent } from './runtime/webRuntime';
|