@n0ts123/mcplink-core 0.0.1

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.js ADDED
@@ -0,0 +1,1410 @@
1
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
3
+ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
4
+ import { generateText, streamText } from 'ai';
5
+ import { z } from 'zod';
6
+
7
+ // src/MCPManager.ts
8
+ var MCPManager = class {
9
+ servers = /* @__PURE__ */ new Map();
10
+ /**
11
+ * 添加 MCP 服务器配置
12
+ */
13
+ addServer(id, config) {
14
+ if (this.servers.has(id)) {
15
+ throw new Error(`MCP server "${id}" already exists`);
16
+ }
17
+ const client = new Client({ name: "mcplink", version: "0.0.1" }, { capabilities: {} });
18
+ let transport;
19
+ if (config.type === "sse") {
20
+ const sseConfig = config;
21
+ transport = new SSEClientTransport(new URL(sseConfig.url));
22
+ } else {
23
+ const stdioConfig = config;
24
+ const processEnv = {};
25
+ for (const [key, value] of Object.entries(process.env)) {
26
+ if (value !== void 0) {
27
+ processEnv[key] = value;
28
+ }
29
+ }
30
+ const mergedEnv = {
31
+ ...processEnv,
32
+ ...stdioConfig.env
33
+ };
34
+ const isWindows = process.platform === "win32";
35
+ let command = stdioConfig.command;
36
+ let args = stdioConfig.args || [];
37
+ if (isWindows) {
38
+ const windowsCommands = ["npx", "npm", "node", "pnpm", "yarn", "bunx"];
39
+ if (windowsCommands.includes(command.toLowerCase())) {
40
+ args = ["/c", command, ...args];
41
+ command = "cmd";
42
+ }
43
+ }
44
+ transport = new StdioClientTransport({
45
+ command,
46
+ args,
47
+ env: mergedEnv
48
+ });
49
+ }
50
+ this.servers.set(id, {
51
+ id,
52
+ config,
53
+ client,
54
+ transport,
55
+ tools: [],
56
+ status: "stopped"
57
+ });
58
+ }
59
+ /**
60
+ * 启动 MCP 服务器
61
+ */
62
+ async startServer(id) {
63
+ const server = this.servers.get(id);
64
+ if (!server) {
65
+ throw new Error(`MCP server "${id}" not found`);
66
+ }
67
+ if (server.status === "running") {
68
+ return;
69
+ }
70
+ server.status = "starting";
71
+ server.error = void 0;
72
+ const config = server.config;
73
+ if (config.type === "stdio") {
74
+ const stdioConfig = config;
75
+ const isWindows = process.platform === "win32";
76
+ const windowsCommands = ["npx", "npm", "node", "pnpm", "yarn", "bunx"];
77
+ let displayCmd = stdioConfig.command;
78
+ let displayArgs = stdioConfig.args || [];
79
+ if (isWindows && windowsCommands.includes(stdioConfig.command.toLowerCase())) {
80
+ displayCmd = "cmd";
81
+ displayArgs = ["/c", stdioConfig.command, ...displayArgs];
82
+ }
83
+ console.log(`
84
+ \u{1F527} [MCP] \u6B63\u5728\u542F\u52A8\u670D\u52A1\u5668 "${id}"...`);
85
+ console.log(` \u547D\u4EE4: ${displayCmd} ${displayArgs.join(" ")}`);
86
+ if (stdioConfig.env && Object.keys(stdioConfig.env).length > 0) {
87
+ console.log(` \u73AF\u5883\u53D8\u91CF: ${Object.keys(stdioConfig.env).join(", ")}`);
88
+ }
89
+ } else {
90
+ const sseConfig = config;
91
+ console.log(`
92
+ \u{1F527} [MCP] \u6B63\u5728\u8FDE\u63A5 SSE \u670D\u52A1\u5668 "${id}"...`);
93
+ console.log(` URL: ${sseConfig.url}`);
94
+ }
95
+ try {
96
+ await server.client.connect(server.transport);
97
+ const toolsResult = await server.client.listTools();
98
+ server.tools = toolsResult.tools.map((tool) => ({
99
+ name: tool.name,
100
+ description: tool.description || "",
101
+ inputSchema: tool.inputSchema
102
+ }));
103
+ server.status = "running";
104
+ console.log(`\u2705 [MCP] \u670D\u52A1\u5668 "${id}" \u542F\u52A8\u6210\u529F\uFF0C\u53D1\u73B0 ${server.tools.length} \u4E2A\u5DE5\u5177`);
105
+ if (server.tools.length > 0) {
106
+ console.log(` \u5DE5\u5177: ${server.tools.map((t) => t.name).join(", ")}`);
107
+ }
108
+ } catch (error) {
109
+ server.status = "error";
110
+ let errorMessage = error instanceof Error ? error.message : String(error);
111
+ if (errorMessage.includes("Connection closed")) {
112
+ if (config.type === "stdio") {
113
+ const stdioConfig = config;
114
+ errorMessage = `MCP \u670D\u52A1\u5668\u542F\u52A8\u5931\u8D25: \u8FDB\u7A0B\u7ACB\u5373\u9000\u51FA\u3002
115
+ \u547D\u4EE4: ${stdioConfig.command} ${(stdioConfig.args || []).join(" ")}
116
+ \u53EF\u80FD\u539F\u56E0:
117
+ 1. \u547D\u4EE4 "${stdioConfig.command}" \u4E0D\u5B58\u5728\u6216\u4E0D\u5728 PATH \u4E2D
118
+ 2. \u5982\u679C\u4F7F\u7528 Docker\uFF0C\u8BF7\u786E\u4FDD Docker \u6B63\u5728\u8FD0\u884C
119
+ 3. \u68C0\u67E5\u73AF\u5883\u53D8\u91CF\u662F\u5426\u6B63\u786E\u914D\u7F6E
120
+ 4. \u5C1D\u8BD5\u5728\u7EC8\u7AEF\u624B\u52A8\u8FD0\u884C\u547D\u4EE4\u67E5\u770B\u5177\u4F53\u9519\u8BEF`;
121
+ }
122
+ }
123
+ console.error(`\u274C [MCP] \u670D\u52A1\u5668 "${id}" \u542F\u52A8\u5931\u8D25:`);
124
+ console.error(` ${errorMessage.split("\n").join("\n ")}`);
125
+ server.error = errorMessage;
126
+ throw new Error(errorMessage);
127
+ }
128
+ }
129
+ /**
130
+ * 停止 MCP 服务器
131
+ */
132
+ async stopServer(id) {
133
+ const server = this.servers.get(id);
134
+ if (!server) {
135
+ throw new Error(`MCP server "${id}" not found`);
136
+ }
137
+ if (server.status === "stopped") {
138
+ return;
139
+ }
140
+ console.log(`\u{1F527} [MCP] \u6B63\u5728\u505C\u6B62\u670D\u52A1\u5668 "${id}"...`);
141
+ try {
142
+ await server.client.close();
143
+ console.log(`\u2705 [MCP] \u670D\u52A1\u5668 "${id}" \u5DF2\u505C\u6B62`);
144
+ } catch (error) {
145
+ console.error(`\u26A0\uFE0F [MCP] \u505C\u6B62\u670D\u52A1\u5668 "${id}" \u65F6\u51FA\u9519:`, error);
146
+ } finally {
147
+ server.status = "stopped";
148
+ server.tools = [];
149
+ }
150
+ }
151
+ /**
152
+ * 启动所有已配置的服务器
153
+ */
154
+ async startAll() {
155
+ const startPromises = Array.from(this.servers.keys()).map(
156
+ (id) => this.startServer(id).catch((error) => {
157
+ console.error(`Failed to start MCP server "${id}":`, error);
158
+ })
159
+ );
160
+ await Promise.all(startPromises);
161
+ }
162
+ /**
163
+ * 停止所有服务器
164
+ */
165
+ async stopAll() {
166
+ const stopPromises = Array.from(this.servers.keys()).map(
167
+ (id) => this.stopServer(id).catch((error) => {
168
+ console.error(`Failed to stop MCP server "${id}":`, error);
169
+ })
170
+ );
171
+ await Promise.all(stopPromises);
172
+ }
173
+ /**
174
+ * 获取所有可用的工具
175
+ */
176
+ getAllTools() {
177
+ const tools = [];
178
+ for (const server of this.servers.values()) {
179
+ if (server.status === "running") {
180
+ tools.push(...server.tools);
181
+ }
182
+ }
183
+ return tools;
184
+ }
185
+ /**
186
+ * 调用工具
187
+ */
188
+ async callTool(toolName, args) {
189
+ for (const server of this.servers.values()) {
190
+ if (server.status !== "running") continue;
191
+ const tool = server.tools.find((t) => t.name === toolName);
192
+ if (tool) {
193
+ const result = await server.client.callTool({
194
+ name: toolName,
195
+ arguments: args
196
+ });
197
+ if (result.content && Array.isArray(result.content)) {
198
+ const textContents = result.content.filter((c) => c.type === "text").map((c) => c.text);
199
+ if (textContents.length > 0) {
200
+ const textResult = textContents.join("\n");
201
+ if (result.isError) {
202
+ throw new Error(textResult || "\u5DE5\u5177\u6267\u884C\u5931\u8D25");
203
+ }
204
+ return textResult;
205
+ }
206
+ }
207
+ if (result.isError) {
208
+ const errorContent = result.content;
209
+ throw new Error(
210
+ typeof errorContent === "string" ? errorContent : JSON.stringify(errorContent) || "\u5DE5\u5177\u6267\u884C\u5931\u8D25"
211
+ );
212
+ }
213
+ return result.content;
214
+ }
215
+ }
216
+ throw new Error(`Tool "${toolName}" not found in any running MCP server`);
217
+ }
218
+ /**
219
+ * 获取所有服务器状态
220
+ */
221
+ getServerStatuses() {
222
+ return Array.from(this.servers.values()).map((server) => ({
223
+ id: server.id,
224
+ name: server.id,
225
+ config: server.config,
226
+ status: server.status,
227
+ tools: server.tools,
228
+ error: server.error
229
+ }));
230
+ }
231
+ /**
232
+ * 移除服务器
233
+ */
234
+ async removeServer(id) {
235
+ await this.stopServer(id);
236
+ this.servers.delete(id);
237
+ }
238
+ };
239
+
240
+ // src/types.ts
241
+ var MCPLinkEventType = /* @__PURE__ */ ((MCPLinkEventType2) => {
242
+ MCPLinkEventType2["THINKING_START"] = "thinking_start";
243
+ MCPLinkEventType2["THINKING_DELTA"] = "thinking_delta";
244
+ MCPLinkEventType2["THINKING_END"] = "thinking_end";
245
+ MCPLinkEventType2["THINKING_CONTENT"] = "thinking_content";
246
+ MCPLinkEventType2["TEXT_START"] = "text_start";
247
+ MCPLinkEventType2["TEXT_DELTA"] = "text_delta";
248
+ MCPLinkEventType2["TEXT_END"] = "text_end";
249
+ MCPLinkEventType2["TOOL_CALL_START"] = "tool_call_start";
250
+ MCPLinkEventType2["TOOL_CALL_DELTA"] = "tool_call_delta";
251
+ MCPLinkEventType2["TOOL_CALL_END"] = "tool_call_end";
252
+ MCPLinkEventType2["TOOL_EXECUTING"] = "tool_executing";
253
+ MCPLinkEventType2["TOOL_RESULT"] = "tool_result";
254
+ MCPLinkEventType2["ITERATION_START"] = "iteration_start";
255
+ MCPLinkEventType2["ITERATION_END"] = "iteration_end";
256
+ MCPLinkEventType2["COMPLETE"] = "complete";
257
+ MCPLinkEventType2["ERROR"] = "error";
258
+ MCPLinkEventType2["TODO_START"] = "todo_start";
259
+ MCPLinkEventType2["TODO_ITEM_ADD"] = "todo_item_add";
260
+ MCPLinkEventType2["TODO_ITEM_UPDATE"] = "todo_item_update";
261
+ MCPLinkEventType2["TODO_END"] = "todo_end";
262
+ return MCPLinkEventType2;
263
+ })(MCPLinkEventType || {});
264
+
265
+ // src/Agent.ts
266
+ var DEFAULT_SYSTEM_PROMPT = `\u4F60\u662F\u4E00\u4E2A\u667A\u80FD\u52A9\u624B\uFF0C\u8BF7\u4F7F\u7528\u4E2D\u6587\u56DE\u590D\u3002`;
267
+ var Agent = class {
268
+ model;
269
+ mcpManager;
270
+ systemPrompt;
271
+ maxIterations;
272
+ constructor(model, mcpManager, options = {}) {
273
+ this.model = model;
274
+ this.mcpManager = mcpManager;
275
+ this.systemPrompt = options.systemPrompt || DEFAULT_SYSTEM_PROMPT;
276
+ this.maxIterations = options.maxIterations || 10;
277
+ }
278
+ /**
279
+ * 将 MCP 工具转换为 Vercel AI SDK 格式
280
+ */
281
+ convertMCPToolsToAITools(mcpTools) {
282
+ const tools = {};
283
+ for (const mcpTool of mcpTools) {
284
+ const zodSchema = this.jsonSchemaToZod(mcpTool.inputSchema);
285
+ tools[mcpTool.name] = {
286
+ description: mcpTool.description,
287
+ parameters: zodSchema
288
+ };
289
+ }
290
+ return tools;
291
+ }
292
+ /**
293
+ * 简单的 JSON Schema 到 Zod 转换
294
+ */
295
+ jsonSchemaToZod(schema) {
296
+ if (!schema.properties) {
297
+ return z.object({});
298
+ }
299
+ const shape = {};
300
+ const required = schema.required || [];
301
+ for (const [key, prop] of Object.entries(schema.properties)) {
302
+ const propSchema = prop;
303
+ let zodType;
304
+ switch (propSchema.type) {
305
+ case "string":
306
+ zodType = z.string();
307
+ break;
308
+ case "number":
309
+ zodType = z.number();
310
+ break;
311
+ case "integer":
312
+ zodType = z.number().int();
313
+ break;
314
+ case "boolean":
315
+ zodType = z.boolean();
316
+ break;
317
+ case "array":
318
+ if (propSchema.items?.type === "string") {
319
+ zodType = z.array(z.string());
320
+ } else if (propSchema.items?.type === "number") {
321
+ zodType = z.array(z.number());
322
+ } else {
323
+ zodType = z.array(z.unknown());
324
+ }
325
+ break;
326
+ default:
327
+ zodType = z.unknown();
328
+ }
329
+ if (propSchema.description) {
330
+ zodType = zodType.describe(propSchema.description);
331
+ }
332
+ if (!required.includes(key)) {
333
+ zodType = zodType.optional();
334
+ }
335
+ shape[key] = zodType;
336
+ }
337
+ return z.object(shape);
338
+ }
339
+ /**
340
+ * 执行对话
341
+ */
342
+ async chat(userMessage, callbacks) {
343
+ const startTime = Date.now();
344
+ const toolCallRecords = [];
345
+ let totalPromptTokens = 0;
346
+ let totalCompletionTokens = 0;
347
+ const messages = [
348
+ { role: "system", content: this.systemPrompt },
349
+ { role: "user", content: userMessage }
350
+ ];
351
+ const mcpTools = this.mcpManager.getAllTools();
352
+ const tools = this.convertMCPToolsToAITools(mcpTools);
353
+ let iteration = 0;
354
+ let finalContent = "";
355
+ while (iteration < this.maxIterations) {
356
+ iteration++;
357
+ callbacks?.onIterationStart?.(iteration);
358
+ const response = await generateText({
359
+ model: this.model,
360
+ messages,
361
+ tools: Object.keys(tools).length > 0 ? tools : void 0,
362
+ maxSteps: 1
363
+ // 每次只执行一步,方便我们控制流程
364
+ });
365
+ if (response.usage) {
366
+ totalPromptTokens += response.usage.promptTokens;
367
+ totalCompletionTokens += response.usage.completionTokens;
368
+ }
369
+ const toolCalls = response.toolCalls || [];
370
+ if (toolCalls.length === 0) {
371
+ finalContent = response.text || "";
372
+ callbacks?.onTextDelta?.(finalContent);
373
+ callbacks?.onIterationEnd?.(iteration);
374
+ break;
375
+ }
376
+ const toolResults = [];
377
+ for (const toolCall of toolCalls) {
378
+ const toolName = toolCall.toolName;
379
+ const toolArgs = toolCall.args;
380
+ const toolCallId = toolCall.toolCallId;
381
+ callbacks?.onToolCallStart?.(toolName, toolArgs);
382
+ const toolStartTime = Date.now();
383
+ let result;
384
+ let isError = false;
385
+ try {
386
+ result = await this.mcpManager.callTool(toolName, toolArgs);
387
+ } catch (error) {
388
+ result = error instanceof Error ? error.message : String(error);
389
+ isError = true;
390
+ }
391
+ const duration2 = Date.now() - toolStartTime;
392
+ callbacks?.onToolResult?.(toolName, result, duration2);
393
+ toolResults.push({
394
+ toolCallId,
395
+ toolName,
396
+ result,
397
+ isError,
398
+ duration: duration2
399
+ });
400
+ toolCallRecords.push({
401
+ name: toolName,
402
+ arguments: toolArgs,
403
+ result,
404
+ duration: duration2
405
+ });
406
+ }
407
+ messages.push({
408
+ role: "assistant",
409
+ content: [
410
+ { type: "text", text: response.text || "" },
411
+ ...toolCalls.map((tc) => ({
412
+ type: "tool-call",
413
+ toolCallId: tc.toolCallId,
414
+ toolName: tc.toolName,
415
+ args: tc.args
416
+ }))
417
+ ]
418
+ });
419
+ for (const tr of toolResults) {
420
+ messages.push({
421
+ role: "tool",
422
+ content: [
423
+ {
424
+ type: "tool-result",
425
+ toolCallId: tr.toolCallId,
426
+ toolName: tr.toolName,
427
+ result: tr.result
428
+ }
429
+ ]
430
+ });
431
+ }
432
+ callbacks?.onIterationEnd?.(iteration);
433
+ }
434
+ const duration = Date.now() - startTime;
435
+ return {
436
+ content: finalContent,
437
+ toolCalls: toolCallRecords,
438
+ messages: messages.map((m) => ({
439
+ role: m.role,
440
+ content: typeof m.content === "string" ? m.content : JSON.stringify(m.content)
441
+ })),
442
+ usage: {
443
+ promptTokens: totalPromptTokens,
444
+ completionTokens: totalCompletionTokens,
445
+ totalTokens: totalPromptTokens + totalCompletionTokens
446
+ },
447
+ iterations: iteration,
448
+ duration
449
+ };
450
+ }
451
+ /**
452
+ * 流式对话 - 返回事件生成器
453
+ * @param userMessage 用户消息
454
+ * @param options 可选参数
455
+ * @param options.allowedTools 允许使用的工具名称列表,为空或不传则使用所有工具
456
+ * @param options.history 历史消息列表
457
+ */
458
+ async *chatStream(userMessage, options) {
459
+ const startTime = Date.now();
460
+ const messages = [{ role: "system", content: this.systemPrompt }];
461
+ if (options?.history && options.history.length > 0) {
462
+ for (const msg of options.history) {
463
+ messages.push({
464
+ role: msg.role,
465
+ content: msg.content
466
+ });
467
+ }
468
+ }
469
+ messages.push({ role: "user", content: userMessage });
470
+ let mcpTools = this.mcpManager.getAllTools();
471
+ if (options?.allowedTools && options.allowedTools.length > 0) {
472
+ mcpTools = mcpTools.filter((tool) => options.allowedTools.includes(tool.name));
473
+ }
474
+ const tools = this.convertMCPToolsToAITools(mcpTools);
475
+ const hasTools = Object.keys(tools).length > 0;
476
+ let iteration = 0;
477
+ while (iteration < this.maxIterations) {
478
+ iteration++;
479
+ yield {
480
+ type: "iteration_start" /* ITERATION_START */,
481
+ timestamp: Date.now(),
482
+ data: { iteration, maxIterations: this.maxIterations }
483
+ };
484
+ const stream = streamText({
485
+ model: this.model,
486
+ messages,
487
+ tools: hasTools ? tools : void 0,
488
+ maxSteps: 1
489
+ });
490
+ let fullText = "";
491
+ let reasoningText = "";
492
+ const toolCalls = [];
493
+ let currentToolCall = null;
494
+ let hasStartedText = false;
495
+ let hasStartedReasoning = false;
496
+ const sentToolCallStarts = /* @__PURE__ */ new Set();
497
+ let isInsideThinkTag = false;
498
+ let textBuffer = "";
499
+ for await (const chunk of stream.fullStream) {
500
+ switch (chunk.type) {
501
+ case "reasoning":
502
+ if (!hasStartedReasoning) {
503
+ hasStartedReasoning = true;
504
+ yield {
505
+ type: "thinking_start" /* THINKING_START */,
506
+ timestamp: Date.now(),
507
+ data: {}
508
+ };
509
+ }
510
+ reasoningText += chunk.textDelta;
511
+ yield {
512
+ type: "thinking_delta" /* THINKING_DELTA */,
513
+ timestamp: Date.now(),
514
+ data: { content: chunk.textDelta }
515
+ };
516
+ break;
517
+ case "text-delta":
518
+ const delta = chunk.textDelta;
519
+ textBuffer += delta;
520
+ if (!isInsideThinkTag) {
521
+ const thinkStartMatch = textBuffer.match(/<think>/i);
522
+ if (thinkStartMatch) {
523
+ const beforeThink = textBuffer.substring(0, thinkStartMatch.index);
524
+ if (beforeThink.trim()) {
525
+ if (!hasStartedText) {
526
+ hasStartedText = true;
527
+ yield {
528
+ type: "text_start" /* TEXT_START */,
529
+ timestamp: Date.now(),
530
+ data: {}
531
+ };
532
+ }
533
+ fullText += beforeThink;
534
+ yield {
535
+ type: "text_delta" /* TEXT_DELTA */,
536
+ timestamp: Date.now(),
537
+ data: { content: beforeThink }
538
+ };
539
+ }
540
+ isInsideThinkTag = true;
541
+ if (!hasStartedReasoning) {
542
+ hasStartedReasoning = true;
543
+ yield {
544
+ type: "thinking_start" /* THINKING_START */,
545
+ timestamp: Date.now(),
546
+ data: {}
547
+ };
548
+ }
549
+ textBuffer = textBuffer.substring(thinkStartMatch.index + 7);
550
+ } else if (!textBuffer.includes("<")) {
551
+ if (hasStartedReasoning && !hasStartedText) {
552
+ yield {
553
+ type: "thinking_end" /* THINKING_END */,
554
+ timestamp: Date.now(),
555
+ data: {}
556
+ };
557
+ }
558
+ if (!hasStartedText) {
559
+ hasStartedText = true;
560
+ yield {
561
+ type: "text_start" /* TEXT_START */,
562
+ timestamp: Date.now(),
563
+ data: {}
564
+ };
565
+ }
566
+ fullText += textBuffer;
567
+ yield {
568
+ type: "text_delta" /* TEXT_DELTA */,
569
+ timestamp: Date.now(),
570
+ data: { content: textBuffer }
571
+ };
572
+ textBuffer = "";
573
+ }
574
+ } else {
575
+ const thinkEndMatch = textBuffer.match(/<\/think>/i);
576
+ if (thinkEndMatch) {
577
+ const thinkContent = textBuffer.substring(0, thinkEndMatch.index);
578
+ if (thinkContent) {
579
+ reasoningText += thinkContent;
580
+ yield {
581
+ type: "thinking_delta" /* THINKING_DELTA */,
582
+ timestamp: Date.now(),
583
+ data: { content: thinkContent }
584
+ };
585
+ }
586
+ yield {
587
+ type: "thinking_end" /* THINKING_END */,
588
+ timestamp: Date.now(),
589
+ data: {}
590
+ };
591
+ isInsideThinkTag = false;
592
+ textBuffer = textBuffer.substring(thinkEndMatch.index + 8);
593
+ } else if (!textBuffer.includes("<")) {
594
+ reasoningText += textBuffer;
595
+ yield {
596
+ type: "thinking_delta" /* THINKING_DELTA */,
597
+ timestamp: Date.now(),
598
+ data: { content: textBuffer }
599
+ };
600
+ textBuffer = "";
601
+ }
602
+ }
603
+ break;
604
+ case "tool-call":
605
+ if (!sentToolCallStarts.has(chunk.toolCallId)) {
606
+ yield {
607
+ type: "tool_call_start" /* TOOL_CALL_START */,
608
+ timestamp: Date.now(),
609
+ data: {
610
+ toolName: chunk.toolName,
611
+ toolCallId: chunk.toolCallId,
612
+ toolArgs: chunk.args
613
+ }
614
+ };
615
+ sentToolCallStarts.add(chunk.toolCallId);
616
+ }
617
+ toolCalls.push({
618
+ toolCallId: chunk.toolCallId,
619
+ toolName: chunk.toolName,
620
+ args: chunk.args
621
+ });
622
+ break;
623
+ case "tool-call-streaming-start":
624
+ currentToolCall = {
625
+ toolCallId: chunk.toolCallId,
626
+ toolName: chunk.toolName,
627
+ argsText: ""
628
+ };
629
+ if (!sentToolCallStarts.has(chunk.toolCallId)) {
630
+ yield {
631
+ type: "tool_call_start" /* TOOL_CALL_START */,
632
+ timestamp: Date.now(),
633
+ data: {
634
+ toolName: chunk.toolName,
635
+ toolCallId: chunk.toolCallId
636
+ }
637
+ };
638
+ sentToolCallStarts.add(chunk.toolCallId);
639
+ }
640
+ break;
641
+ case "tool-call-delta":
642
+ if (currentToolCall) {
643
+ currentToolCall.argsText += chunk.argsTextDelta;
644
+ yield {
645
+ type: "tool_call_delta" /* TOOL_CALL_DELTA */,
646
+ timestamp: Date.now(),
647
+ data: {
648
+ toolCallId: currentToolCall.toolCallId,
649
+ argsTextDelta: chunk.argsTextDelta
650
+ }
651
+ };
652
+ }
653
+ break;
654
+ case "finish":
655
+ if (textBuffer) {
656
+ if (isInsideThinkTag) {
657
+ reasoningText += textBuffer;
658
+ yield {
659
+ type: "thinking_delta" /* THINKING_DELTA */,
660
+ timestamp: Date.now(),
661
+ data: { content: textBuffer }
662
+ };
663
+ } else {
664
+ if (!hasStartedText) {
665
+ hasStartedText = true;
666
+ yield {
667
+ type: "text_start" /* TEXT_START */,
668
+ timestamp: Date.now(),
669
+ data: {}
670
+ };
671
+ }
672
+ fullText += textBuffer;
673
+ yield {
674
+ type: "text_delta" /* TEXT_DELTA */,
675
+ timestamp: Date.now(),
676
+ data: { content: textBuffer }
677
+ };
678
+ }
679
+ textBuffer = "";
680
+ }
681
+ if (isInsideThinkTag || hasStartedReasoning && !hasStartedText) {
682
+ yield {
683
+ type: "thinking_end" /* THINKING_END */,
684
+ timestamp: Date.now(),
685
+ data: {}
686
+ };
687
+ isInsideThinkTag = false;
688
+ }
689
+ if (hasStartedText) {
690
+ yield {
691
+ type: "text_end" /* TEXT_END */,
692
+ timestamp: Date.now(),
693
+ data: {}
694
+ };
695
+ }
696
+ break;
697
+ case "error":
698
+ yield {
699
+ type: "error" /* ERROR */,
700
+ timestamp: Date.now(),
701
+ data: { error: chunk.error }
702
+ };
703
+ break;
704
+ }
705
+ }
706
+ if (toolCalls.length === 0) {
707
+ yield {
708
+ type: "iteration_end" /* ITERATION_END */,
709
+ timestamp: Date.now(),
710
+ data: { iteration }
711
+ };
712
+ break;
713
+ }
714
+ if (fullText) {
715
+ yield {
716
+ type: "thinking_content" /* THINKING_CONTENT */,
717
+ timestamp: Date.now(),
718
+ data: { content: fullText }
719
+ };
720
+ }
721
+ const toolResults = [];
722
+ for (const toolCall of toolCalls) {
723
+ const toolName = toolCall.toolName;
724
+ const toolArgs = toolCall.args;
725
+ const toolCallId = toolCall.toolCallId;
726
+ yield {
727
+ type: "tool_executing" /* TOOL_EXECUTING */,
728
+ timestamp: Date.now(),
729
+ data: { toolName, toolCallId, toolArgs }
730
+ };
731
+ const toolStartTime = Date.now();
732
+ let result;
733
+ let isError = false;
734
+ try {
735
+ result = await this.mcpManager.callTool(toolName, toolArgs);
736
+ } catch (error) {
737
+ result = error instanceof Error ? error.message : String(error);
738
+ isError = true;
739
+ }
740
+ const duration = Date.now() - toolStartTime;
741
+ yield {
742
+ type: "tool_result" /* TOOL_RESULT */,
743
+ timestamp: Date.now(),
744
+ data: {
745
+ toolName,
746
+ toolResult: result,
747
+ toolCallId,
748
+ duration,
749
+ isError
750
+ }
751
+ };
752
+ toolResults.push({
753
+ toolCallId,
754
+ toolName,
755
+ result,
756
+ isError,
757
+ duration
758
+ });
759
+ }
760
+ messages.push({
761
+ role: "assistant",
762
+ content: [
763
+ ...fullText ? [{ type: "text", text: fullText }] : [],
764
+ ...toolCalls.map((tc) => ({
765
+ type: "tool-call",
766
+ toolCallId: tc.toolCallId,
767
+ toolName: tc.toolName,
768
+ args: tc.args
769
+ }))
770
+ ]
771
+ });
772
+ for (const tr of toolResults) {
773
+ messages.push({
774
+ role: "tool",
775
+ content: [
776
+ {
777
+ type: "tool-result",
778
+ toolCallId: tr.toolCallId,
779
+ toolName: tr.toolName,
780
+ result: tr.result
781
+ }
782
+ ]
783
+ });
784
+ }
785
+ yield {
786
+ type: "iteration_end" /* ITERATION_END */,
787
+ timestamp: Date.now(),
788
+ data: { iteration }
789
+ };
790
+ }
791
+ const totalDuration = Date.now() - startTime;
792
+ yield {
793
+ type: "complete" /* COMPLETE */,
794
+ timestamp: Date.now(),
795
+ data: {
796
+ totalIterations: iteration,
797
+ totalDuration
798
+ }
799
+ };
800
+ }
801
+ };
802
+ var PromptBasedAgent = class {
803
+ model;
804
+ mcpManager;
805
+ systemPrompt;
806
+ maxIterations;
807
+ constructor(model, mcpManager, options = {}) {
808
+ this.model = model;
809
+ this.mcpManager = mcpManager;
810
+ this.systemPrompt = options.systemPrompt || "";
811
+ this.maxIterations = options.maxIterations || 10;
812
+ }
813
+ /**
814
+ * 生成工具列表描述
815
+ */
816
+ generateToolsDescription(tools) {
817
+ if (tools.length === 0) {
818
+ return "\u5F53\u524D\u6CA1\u6709\u53EF\u7528\u7684\u5DE5\u5177\u3002";
819
+ }
820
+ let description = "";
821
+ for (const tool of tools) {
822
+ description += `### ${tool.name}
823
+ `;
824
+ description += `\u63CF\u8FF0: ${tool.description}
825
+ `;
826
+ description += `\u53C2\u6570: ${JSON.stringify(tool.inputSchema, null, 2)}
827
+
828
+ `;
829
+ }
830
+ return description;
831
+ }
832
+ /**
833
+ * 内置系统提示词 - 强调格式约束
834
+ */
835
+ BUILT_IN_PROMPT = `
836
+ ## \u5DE5\u5177\u8C03\u7528\u683C\u5F0F\uFF08\u5FC5\u987B\u4E25\u683C\u9075\u5B88\uFF09
837
+
838
+ \u5F53\u4F60\u9700\u8981\u83B7\u53D6\u6570\u636E\u6216\u6267\u884C\u64CD\u4F5C\u65F6\uFF0C**\u53EA\u80FD**\u4F7F\u7528\u4EE5\u4E0B\u683C\u5F0F\uFF1A
839
+
840
+ <tool_call>
841
+ {"name": "\u5DE5\u5177\u540D\u79F0", "arguments": {"\u53C2\u6570\u540D": "\u503C"}}
842
+ </tool_call>
843
+
844
+ ### \u5DE5\u4F5C\u6D41\u7A0B
845
+ 1. \u5206\u6790\u7528\u6237\u9700\u6C42
846
+ 2. \u5982\u9700\u6570\u636E\uFF0C\u8F93\u51FA <tool_call>...</tool_call> \u540E**\u7ACB\u5373\u505C\u6B62**
847
+ 3. \u7CFB\u7EDF\u4F1A\u6267\u884C\u5DE5\u5177\u5E76\u8FD4\u56DE\u771F\u5B9E\u7ED3\u679C
848
+ 4. \u6536\u5230\u7ED3\u679C\u540E\uFF0C\u7528\u4E2D\u6587\u6574\u7406\u56DE\u590D\u7528\u6237
849
+
850
+ ### \u4E25\u683C\u7981\u6B62
851
+ - \u274C \u81EA\u5DF1\u7F16\u5199\u5DE5\u5177\u8FD4\u56DE\u7ED3\u679C\uFF08\u5982 \`\u7ED3\u679C:{...}\` \u6216 \`{"code":200...}\`\uFF09
852
+ - \u274C \u6A21\u62DF\u5DE5\u5177\u8C03\u7528\uFF08\u5982 \`RPCCall:\`\u3001\`FunctionCall:\`\uFF09
853
+ - \u274C \u5728\u6CA1\u6709\u771F\u5B9E\u5DE5\u5177\u7ED3\u679C\u7684\u60C5\u51B5\u4E0B\u7F16\u9020\u6570\u636E
854
+ - \u274C \u4E00\u6B21\u8F93\u51FA\u4E2D\u540C\u65F6\u5305\u542B\u5DE5\u5177\u8C03\u7528\u548C\u6700\u7EC8\u56DE\u590D
855
+
856
+ ### \u6B63\u786E\u793A\u4F8B
857
+ \u7528\u6237: "\u67E5\u8BE2\u6211\u7684\u8BA2\u5355"
858
+ \u4F60\u7684\u8F93\u51FA:
859
+ <tool_call>
860
+ {"name": "get_orders", "arguments": {"token": "xxx"}}
861
+ </tool_call>
862
+
863
+ \uFF08\u7136\u540E\u505C\u6B62\uFF0C\u7B49\u5F85\u7CFB\u7EDF\u8FD4\u56DE\u771F\u5B9E\u7ED3\u679C\uFF09
864
+
865
+ ### \u56DE\u590D\u683C\u5F0F
866
+ - \u4F7F\u7528\u4E2D\u6587
867
+ - \u4F7F\u7528 Markdown \u683C\u5F0F\u7F8E\u5316\u8F93\u51FA
868
+ - \u5217\u8868\u6570\u636E\u6BCF\u9879\u72EC\u5360\u4E00\u884C
869
+ `;
870
+ /**
871
+ * 构建完整的系统提示词
872
+ */
873
+ buildSystemPrompt(tools) {
874
+ const toolsDescription = this.generateToolsDescription(tools);
875
+ const userPrompt = this.systemPrompt || "\u4F60\u662F\u4E00\u4E2A\u667A\u80FD\u52A9\u624B\u3002";
876
+ return `${userPrompt}
877
+
878
+ ## \u53EF\u7528\u5DE5\u5177
879
+ ${toolsDescription}
880
+ ${this.BUILT_IN_PROMPT}`;
881
+ }
882
+ /**
883
+ * 解析工具调用
884
+ */
885
+ parseToolCall(text) {
886
+ const tagMatch = text.match(/<tool_call>\s*([\s\S]*?)\s*<\/tool_call>/i);
887
+ if (tagMatch) {
888
+ try {
889
+ const json = JSON.parse(tagMatch[1].trim());
890
+ if (json.name) return { name: json.name, arguments: json.arguments || {} };
891
+ } catch {
892
+ }
893
+ }
894
+ const codeMatch = text.match(/```(?:json)?\s*\n?\s*(\{[\s\S]*?"name"[\s\S]*?\})\s*\n?\s*```/i);
895
+ if (codeMatch) {
896
+ try {
897
+ const json = JSON.parse(codeMatch[1].trim());
898
+ if (json.name) return { name: json.name, arguments: json.arguments || {} };
899
+ } catch {
900
+ }
901
+ }
902
+ const jsonMatch = text.match(/\{\s*"name"\s*:\s*"([^"]+)"[\s\S]*?"arguments"\s*:\s*(\{[\s\S]*?\})\s*\}/i);
903
+ if (jsonMatch) {
904
+ try {
905
+ const fullMatch = jsonMatch[0];
906
+ const json = JSON.parse(fullMatch);
907
+ if (json.name) return { name: json.name, arguments: json.arguments || {} };
908
+ } catch {
909
+ }
910
+ }
911
+ return null;
912
+ }
913
+ /**
914
+ * 智能压缩历史消息
915
+ * - 用户消息完整保留
916
+ * - AI 回复保留关键信息(ID、名称、数量、价格等)
917
+ * - 去除冗长的 JSON 原始数据
918
+ */
919
+ compressHistory(history) {
920
+ const MAX_USER_MESSAGE_LENGTH = 500;
921
+ const MAX_ASSISTANT_MESSAGE_LENGTH = 1500;
922
+ const recentHistory = history.slice(-20);
923
+ return recentHistory.map((msg) => {
924
+ if (msg.role === "user") {
925
+ if (msg.content.length <= MAX_USER_MESSAGE_LENGTH) {
926
+ return msg;
927
+ }
928
+ return {
929
+ role: msg.role,
930
+ content: msg.content.slice(0, MAX_USER_MESSAGE_LENGTH) + "..."
931
+ };
932
+ }
933
+ let content = msg.content;
934
+ content = content.replace(/```json\n[\s\S]*?\n```/g, "[\u5DE5\u5177\u8FD4\u56DE\u6570\u636E]");
935
+ content = content.replace(/## .*?\(原始JSON\)[\s\S]*?(?=##|$)/g, "");
936
+ if (content.length > MAX_ASSISTANT_MESSAGE_LENGTH) {
937
+ const tableMatch = content.match(/\|[\s\S]*?\|/g);
938
+ if (tableMatch) {
939
+ const tables = tableMatch.join("\n");
940
+ if (tables.length < MAX_ASSISTANT_MESSAGE_LENGTH) {
941
+ content = content.slice(0, MAX_ASSISTANT_MESSAGE_LENGTH - tables.length) + "\n" + tables;
942
+ }
943
+ }
944
+ content = content.slice(0, MAX_ASSISTANT_MESSAGE_LENGTH) + "...";
945
+ }
946
+ return { role: msg.role, content: content.trim() || msg.content.slice(0, 500) };
947
+ });
948
+ }
949
+ /**
950
+ * 流式对话
951
+ */
952
+ async *chatStream(userMessage, options) {
953
+ const startTime = Date.now();
954
+ let mcpTools = this.mcpManager.getAllTools();
955
+ if (options?.allowedTools?.length) {
956
+ mcpTools = mcpTools.filter((t) => options.allowedTools.includes(t.name));
957
+ }
958
+ const messages = [
959
+ { role: "system", content: this.buildSystemPrompt(mcpTools) }
960
+ ];
961
+ if (options?.history?.length) {
962
+ const compressedHistory = this.compressHistory(options.history);
963
+ console.log(`[PromptBasedAgent] \u{1F4DA} \u5386\u53F2\u6D88\u606F: ${options.history.length} \u6761 -> \u538B\u7F29\u540E: ${compressedHistory.length} \u6761`);
964
+ for (const msg of compressedHistory) {
965
+ messages.push({ role: msg.role, content: msg.content });
966
+ }
967
+ }
968
+ messages.push({ role: "user", content: userMessage });
969
+ console.log(`[PromptBasedAgent] \u{1F4DD} \u7528\u6237\u6D88\u606F: "${userMessage.slice(0, 50)}${userMessage.length > 50 ? "..." : ""}"`);
970
+ console.log(`[PromptBasedAgent] \u{1F4CA} \u603B\u6D88\u606F\u6570: ${messages.length}`);
971
+ let iteration = 0;
972
+ while (iteration < this.maxIterations) {
973
+ iteration++;
974
+ yield {
975
+ type: "iteration_start" /* ITERATION_START */,
976
+ timestamp: Date.now(),
977
+ data: { iteration, maxIterations: this.maxIterations }
978
+ };
979
+ console.log(`[PromptBasedAgent] \u{1F916} \u8C03\u7528\u6A21\u578B\uFF0C\u8FED\u4EE3 ${iteration}/${this.maxIterations}...`);
980
+ const modelStartTime = Date.now();
981
+ const stream = streamText({
982
+ model: this.model,
983
+ messages,
984
+ // 设置请求超时
985
+ experimental_telemetry: {
986
+ isEnabled: false
987
+ // 禁用遥测以减少开销
988
+ }
989
+ });
990
+ let fullResponse = "";
991
+ let buffer = "";
992
+ let inThinking = false;
993
+ let inToolCall = false;
994
+ let thinkingStarted = false;
995
+ let thinkingEnded = false;
996
+ let textStarted = false;
997
+ let firstChunkReceived = false;
998
+ const FIRST_CHUNK_TIMEOUT = 12e4;
999
+ let timeoutId = null;
1000
+ new Promise((_, reject) => {
1001
+ timeoutId = setTimeout(() => {
1002
+ reject(new Error(`\u6A21\u578B\u54CD\u5E94\u8D85\u65F6 (${FIRST_CHUNK_TIMEOUT / 1e3}\u79D2\u65E0\u54CD\u5E94)`));
1003
+ }, FIRST_CHUNK_TIMEOUT);
1004
+ });
1005
+ try {
1006
+ for await (const chunk of stream.fullStream) {
1007
+ if (!firstChunkReceived) {
1008
+ firstChunkReceived = true;
1009
+ if (timeoutId) {
1010
+ clearTimeout(timeoutId);
1011
+ timeoutId = null;
1012
+ }
1013
+ console.log(`[PromptBasedAgent] \u26A1 \u9996\u4E2A chunk \u5230\u8FBE\uFF0C\u8017\u65F6: ${Date.now() - modelStartTime}ms`);
1014
+ }
1015
+ if (chunk.type === "reasoning") {
1016
+ if (!thinkingStarted) {
1017
+ thinkingStarted = true;
1018
+ yield { type: "thinking_start" /* THINKING_START */, timestamp: Date.now(), data: {} };
1019
+ }
1020
+ if (chunk.textDelta) {
1021
+ yield { type: "thinking_delta" /* THINKING_DELTA */, timestamp: Date.now(), data: { content: chunk.textDelta } };
1022
+ }
1023
+ continue;
1024
+ }
1025
+ if (chunk.type === "text-delta") {
1026
+ const delta = chunk.textDelta;
1027
+ buffer += delta;
1028
+ fullResponse += delta;
1029
+ while (buffer.length > 0) {
1030
+ if (!inThinking && !inToolCall) {
1031
+ const thinkStart = buffer.indexOf("<think>");
1032
+ if (thinkStart !== -1) {
1033
+ if (thinkStart > 0) {
1034
+ const before = buffer.substring(0, thinkStart);
1035
+ if (before.trim() && thinkingEnded) {
1036
+ if (!textStarted) {
1037
+ textStarted = true;
1038
+ yield { type: "text_start" /* TEXT_START */, timestamp: Date.now(), data: {} };
1039
+ }
1040
+ yield { type: "text_delta" /* TEXT_DELTA */, timestamp: Date.now(), data: { content: before } };
1041
+ }
1042
+ }
1043
+ inThinking = true;
1044
+ if (!thinkingStarted) {
1045
+ thinkingStarted = true;
1046
+ yield { type: "thinking_start" /* THINKING_START */, timestamp: Date.now(), data: {} };
1047
+ }
1048
+ buffer = buffer.substring(thinkStart + 7);
1049
+ continue;
1050
+ }
1051
+ const toolStart = buffer.indexOf("<tool_call>");
1052
+ if (toolStart !== -1) {
1053
+ if (toolStart > 0) {
1054
+ const before = buffer.substring(0, toolStart);
1055
+ if (before.trim() && thinkingEnded) {
1056
+ if (!textStarted) {
1057
+ textStarted = true;
1058
+ yield { type: "text_start" /* TEXT_START */, timestamp: Date.now(), data: {} };
1059
+ }
1060
+ yield { type: "text_delta" /* TEXT_DELTA */, timestamp: Date.now(), data: { content: before } };
1061
+ }
1062
+ }
1063
+ inToolCall = true;
1064
+ buffer = buffer.substring(toolStart + 11);
1065
+ continue;
1066
+ }
1067
+ if (!buffer.includes("<")) {
1068
+ if (buffer.trim() && (thinkingEnded || !thinkingStarted)) {
1069
+ if (!thinkingStarted && !thinkingEnded) {
1070
+ thinkingStarted = true;
1071
+ thinkingEnded = true;
1072
+ yield { type: "thinking_start" /* THINKING_START */, timestamp: Date.now(), data: {} };
1073
+ yield { type: "thinking_delta" /* THINKING_DELTA */, timestamp: Date.now(), data: { content: "\u5206\u6790\u7528\u6237\u8BF7\u6C42..." } };
1074
+ yield { type: "thinking_end" /* THINKING_END */, timestamp: Date.now(), data: {} };
1075
+ }
1076
+ if (!textStarted) {
1077
+ textStarted = true;
1078
+ yield { type: "text_start" /* TEXT_START */, timestamp: Date.now(), data: {} };
1079
+ }
1080
+ yield { type: "text_delta" /* TEXT_DELTA */, timestamp: Date.now(), data: { content: buffer } };
1081
+ }
1082
+ buffer = "";
1083
+ }
1084
+ break;
1085
+ }
1086
+ if (inThinking) {
1087
+ const thinkEnd = buffer.indexOf("</think>");
1088
+ if (thinkEnd !== -1) {
1089
+ const content = buffer.substring(0, thinkEnd);
1090
+ if (content) {
1091
+ yield { type: "thinking_delta" /* THINKING_DELTA */, timestamp: Date.now(), data: { content } };
1092
+ }
1093
+ yield { type: "thinking_end" /* THINKING_END */, timestamp: Date.now(), data: {} };
1094
+ thinkingEnded = true;
1095
+ inThinking = false;
1096
+ buffer = buffer.substring(thinkEnd + 8);
1097
+ continue;
1098
+ }
1099
+ if (buffer.length > 10 && !buffer.includes("<")) {
1100
+ const safe = buffer.substring(0, buffer.length - 10);
1101
+ yield { type: "thinking_delta" /* THINKING_DELTA */, timestamp: Date.now(), data: { content: safe } };
1102
+ buffer = buffer.substring(safe.length);
1103
+ }
1104
+ break;
1105
+ }
1106
+ if (inToolCall) {
1107
+ const toolEnd = buffer.indexOf("</tool_call>");
1108
+ if (toolEnd !== -1) {
1109
+ inToolCall = false;
1110
+ buffer = buffer.substring(toolEnd + 12);
1111
+ continue;
1112
+ }
1113
+ break;
1114
+ }
1115
+ break;
1116
+ }
1117
+ }
1118
+ if (chunk.type === "finish") {
1119
+ if (buffer.trim()) {
1120
+ if (inThinking) {
1121
+ yield { type: "thinking_delta" /* THINKING_DELTA */, timestamp: Date.now(), data: { content: buffer } };
1122
+ yield { type: "thinking_end" /* THINKING_END */, timestamp: Date.now(), data: {} };
1123
+ thinkingEnded = true;
1124
+ } else if (!inToolCall) {
1125
+ if (!textStarted) {
1126
+ textStarted = true;
1127
+ yield { type: "text_start" /* TEXT_START */, timestamp: Date.now(), data: {} };
1128
+ }
1129
+ yield { type: "text_delta" /* TEXT_DELTA */, timestamp: Date.now(), data: { content: buffer } };
1130
+ }
1131
+ }
1132
+ if (textStarted) {
1133
+ yield { type: "text_end" /* TEXT_END */, timestamp: Date.now(), data: {} };
1134
+ }
1135
+ }
1136
+ }
1137
+ } finally {
1138
+ if (timeoutId) {
1139
+ clearTimeout(timeoutId);
1140
+ }
1141
+ }
1142
+ const toolCall = this.parseToolCall(fullResponse);
1143
+ if (toolCall) {
1144
+ const toolCallId = `tool-${Date.now()}`;
1145
+ yield {
1146
+ type: "tool_call_start" /* TOOL_CALL_START */,
1147
+ timestamp: Date.now(),
1148
+ data: { toolName: toolCall.name, toolCallId, toolArgs: toolCall.arguments }
1149
+ };
1150
+ yield {
1151
+ type: "tool_executing" /* TOOL_EXECUTING */,
1152
+ timestamp: Date.now(),
1153
+ data: { toolName: toolCall.name, toolCallId, toolArgs: toolCall.arguments }
1154
+ };
1155
+ const toolStartTime = Date.now();
1156
+ let result;
1157
+ let isError = false;
1158
+ try {
1159
+ result = await this.mcpManager.callTool(toolCall.name, toolCall.arguments);
1160
+ } catch (error) {
1161
+ result = error instanceof Error ? error.message : String(error);
1162
+ isError = true;
1163
+ }
1164
+ const duration = Date.now() - toolStartTime;
1165
+ yield {
1166
+ type: "tool_result" /* TOOL_RESULT */,
1167
+ timestamp: Date.now(),
1168
+ data: { toolName: toolCall.name, toolResult: result, toolCallId, duration, isError }
1169
+ };
1170
+ messages.push({ role: "assistant", content: fullResponse });
1171
+ const resultStr = typeof result === "string" ? result : JSON.stringify(result, null, 2);
1172
+ messages.push({
1173
+ role: "user",
1174
+ content: `\u5DE5\u5177 ${toolCall.name} \u8FD4\u56DE\u7ED3\u679C\uFF1A
1175
+ ${resultStr}
1176
+
1177
+ \u8BF7\u6839\u636E\u7ED3\u679C\u7528\u4E2D\u6587\u56DE\u590D\u7528\u6237\u3002`
1178
+ });
1179
+ yield { type: "iteration_end" /* ITERATION_END */, timestamp: Date.now(), data: { iteration } };
1180
+ continue;
1181
+ }
1182
+ console.log(`[PromptBasedAgent] \u2705 \u6A21\u578B\u54CD\u5E94\u5B8C\u6210\uFF0C\u8017\u65F6: ${Date.now() - modelStartTime}ms\uFF0C\u54CD\u5E94\u957F\u5EA6: ${fullResponse.length}`);
1183
+ if (!textStarted && fullResponse.trim()) {
1184
+ let cleanText = fullResponse.replace(/<think>[\s\S]*?<\/think>/gi, "").replace(/<tool_call>[\s\S]*?<\/tool_call>/gi, "").trim();
1185
+ if (cleanText) {
1186
+ yield { type: "text_start" /* TEXT_START */, timestamp: Date.now(), data: {} };
1187
+ yield { type: "text_delta" /* TEXT_DELTA */, timestamp: Date.now(), data: { content: cleanText } };
1188
+ yield { type: "text_end" /* TEXT_END */, timestamp: Date.now(), data: {} };
1189
+ }
1190
+ }
1191
+ yield { type: "iteration_end" /* ITERATION_END */, timestamp: Date.now(), data: { iteration } };
1192
+ break;
1193
+ }
1194
+ yield {
1195
+ type: "complete" /* COMPLETE */,
1196
+ timestamp: Date.now(),
1197
+ data: { totalDuration: Date.now() - startTime, totalIterations: iteration }
1198
+ };
1199
+ }
1200
+ };
1201
+
1202
+ // src/MCPLink.ts
1203
+ var NATIVE_FUNCTION_CALLING_PATTERNS = [
1204
+ // OpenAI GPT 系列 - 支持原生 function calling
1205
+ /^gpt/i,
1206
+ // OpenAI o1/o3 需要特殊处理,暂用 PromptBased
1207
+ // /^o1/i,
1208
+ // /^o3/i,
1209
+ // Anthropic Claude - 支持原生 function calling
1210
+ /^claude/i,
1211
+ // Google Gemini 稳定版 - 支持原生 function calling
1212
+ // 注意:gemini-*-preview/thinking 版本需要特殊处理,不在此列表
1213
+ /^gemini-[\d.]+-flash$/i,
1214
+ /^gemini-[\d.]+-pro$/i,
1215
+ /^gemini-pro$/i,
1216
+ /^gemini-flash$/i,
1217
+ // Mistral - 支持原生 function calling
1218
+ /^mistral/i,
1219
+ /^mixtral/i,
1220
+ // Cohere Command-R - 支持原生 function calling
1221
+ /^command-r/i
1222
+ ];
1223
+ var PROMPT_BASED_PATTERNS = [
1224
+ // DeepSeek(不支持原生 function calling)
1225
+ /deepseek/i,
1226
+ // OpenAI o1/o3 思考模型
1227
+ /^o1/i,
1228
+ /^o3/i,
1229
+ // Gemini 思考/预览版本 - 需要 thought_signature,暂用 PromptBased
1230
+ /gemini.*preview/i,
1231
+ /gemini.*thinking/i,
1232
+ /gemini.*exp/i,
1233
+ // 开源模型(大多数不支持原生 function calling)
1234
+ /^llama/i,
1235
+ /^phi-/i,
1236
+ /^qwen/i,
1237
+ /^yi-/i,
1238
+ /^glm/i,
1239
+ /^baichuan/i
1240
+ ];
1241
+ function detectNativeToolSupport(modelId) {
1242
+ console.log(`[MCPLink] \u{1F50D} \u68C0\u6D4B\u6A21\u578B: "${modelId}"`);
1243
+ for (const pattern of PROMPT_BASED_PATTERNS) {
1244
+ if (pattern.test(modelId)) {
1245
+ console.log(`[MCPLink] \u2705 Model "${modelId}" -> PromptBasedAgent (matched: ${pattern})`);
1246
+ return false;
1247
+ }
1248
+ }
1249
+ for (const pattern of NATIVE_FUNCTION_CALLING_PATTERNS) {
1250
+ if (pattern.test(modelId)) {
1251
+ console.log(`[MCPLink] \u2705 Model "${modelId}" -> Agent (\u539F\u751F\u6A21\u5F0F, matched: ${pattern})`);
1252
+ return true;
1253
+ }
1254
+ }
1255
+ console.log(`[MCPLink] \u26A0\uFE0F Model "${modelId}" -> PromptBasedAgent (\u672A\u77E5\u6A21\u578B\uFF0C\u9ED8\u8BA4)`);
1256
+ return false;
1257
+ }
1258
+ var MCPLink = class {
1259
+ model;
1260
+ mcpManager;
1261
+ agent;
1262
+ promptBasedAgent;
1263
+ config;
1264
+ initialized = false;
1265
+ detectedNativeSupport;
1266
+ constructor(config) {
1267
+ this.config = config;
1268
+ this.model = config.model;
1269
+ this.mcpManager = new MCPManager();
1270
+ if (config.mcpServers) {
1271
+ for (const [id, serverConfig] of Object.entries(config.mcpServers)) {
1272
+ this.mcpManager.addServer(id, serverConfig);
1273
+ }
1274
+ }
1275
+ this.agent = new Agent(this.model, this.mcpManager, {
1276
+ systemPrompt: config.systemPrompt,
1277
+ maxIterations: config.maxIterations
1278
+ });
1279
+ this.promptBasedAgent = new PromptBasedAgent(this.model, this.mcpManager, {
1280
+ systemPrompt: config.systemPrompt,
1281
+ maxIterations: config.maxIterations
1282
+ });
1283
+ if (config.usePromptBasedTools === true) {
1284
+ this.detectedNativeSupport = false;
1285
+ } else if (config.usePromptBasedTools === false) {
1286
+ this.detectedNativeSupport = true;
1287
+ } else {
1288
+ const modelNameToCheck = config.modelName || config.model.modelId;
1289
+ this.detectedNativeSupport = detectNativeToolSupport(modelNameToCheck);
1290
+ }
1291
+ }
1292
+ /**
1293
+ * 初始化 - 连接所有 MCP 服务器
1294
+ */
1295
+ async initialize() {
1296
+ if (this.initialized) {
1297
+ return;
1298
+ }
1299
+ await this.mcpManager.startAll();
1300
+ this.initialized = true;
1301
+ }
1302
+ /**
1303
+ * 关闭 - 断开所有 MCP 服务器连接
1304
+ */
1305
+ async close() {
1306
+ await this.mcpManager.stopAll();
1307
+ this.initialized = false;
1308
+ }
1309
+ /**
1310
+ * 发起对话
1311
+ */
1312
+ async chat(message, callbacks) {
1313
+ if (!this.initialized) {
1314
+ await this.initialize();
1315
+ }
1316
+ return this.agent.chat(message, callbacks);
1317
+ }
1318
+ /**
1319
+ * 流式对话
1320
+ * @param message 用户消息
1321
+ * @param options 可选参数
1322
+ * @param options.allowedTools 允许使用的工具名称列表
1323
+ * @param options.history 历史消息列表
1324
+ */
1325
+ async *chatStream(message, options) {
1326
+ if (!this.initialized) {
1327
+ await this.initialize();
1328
+ }
1329
+ if (this.detectedNativeSupport) {
1330
+ yield* this.agent.chatStream(message, options);
1331
+ } else {
1332
+ yield* this.promptBasedAgent.chatStream(message, options);
1333
+ }
1334
+ }
1335
+ /**
1336
+ * 获取当前使用的模式
1337
+ */
1338
+ getToolCallingMode() {
1339
+ return this.detectedNativeSupport ? "native" : "prompt-based";
1340
+ }
1341
+ // ============ MCP 服务器管理 ============
1342
+ /**
1343
+ * 添加 MCP 服务器
1344
+ */
1345
+ addMCPServer(id, config) {
1346
+ this.mcpManager.addServer(id, config);
1347
+ }
1348
+ /**
1349
+ * 移除 MCP 服务器
1350
+ */
1351
+ async removeMCPServer(id) {
1352
+ await this.mcpManager.removeServer(id);
1353
+ }
1354
+ /**
1355
+ * 启动指定 MCP 服务器
1356
+ */
1357
+ async startMCPServer(id) {
1358
+ await this.mcpManager.startServer(id);
1359
+ }
1360
+ /**
1361
+ * 停止指定 MCP 服务器
1362
+ */
1363
+ async stopMCPServer(id) {
1364
+ await this.mcpManager.stopServer(id);
1365
+ }
1366
+ /**
1367
+ * 获取所有 MCP 服务器状态
1368
+ */
1369
+ getMCPServerStatuses() {
1370
+ return this.mcpManager.getServerStatuses();
1371
+ }
1372
+ /**
1373
+ * 获取所有可用工具
1374
+ */
1375
+ getTools() {
1376
+ return this.mcpManager.getAllTools();
1377
+ }
1378
+ /**
1379
+ * 手动调用工具
1380
+ */
1381
+ async callTool(toolName, args) {
1382
+ return this.mcpManager.callTool(toolName, args);
1383
+ }
1384
+ // ============ 配置管理 ============
1385
+ /**
1386
+ * 更新系统提示词
1387
+ */
1388
+ setSystemPrompt(prompt) {
1389
+ this.config.systemPrompt = prompt;
1390
+ this.agent = new Agent(this.model, this.mcpManager, {
1391
+ systemPrompt: prompt,
1392
+ maxIterations: this.config.maxIterations
1393
+ });
1394
+ }
1395
+ /**
1396
+ * 更新 AI 模型
1397
+ */
1398
+ setModel(model) {
1399
+ this.model = model;
1400
+ this.config.model = model;
1401
+ this.agent = new Agent(this.model, this.mcpManager, {
1402
+ systemPrompt: this.config.systemPrompt,
1403
+ maxIterations: this.config.maxIterations
1404
+ });
1405
+ }
1406
+ };
1407
+
1408
+ export { Agent, DEFAULT_SYSTEM_PROMPT, MCPLink, MCPLinkEventType, MCPManager, PromptBasedAgent };
1409
+ //# sourceMappingURL=index.js.map
1410
+ //# sourceMappingURL=index.js.map