@next-open-ai/openbot 0.6.8 → 0.6.16
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 +156 -137
- package/apps/desktop/renderer/dist/assets/index-BxqMW-uy.css +10 -0
- package/apps/desktop/renderer/dist/assets/index-DJs-wX3R.js +89 -0
- package/apps/desktop/renderer/dist/index.html +2 -2
- package/dist/core/agent/agent-manager.d.ts +14 -6
- package/dist/core/agent/agent-manager.js +63 -23
- package/dist/core/agent/proxy/adapters/coze-adapter.d.ts +2 -0
- package/dist/core/agent/proxy/adapters/coze-adapter.js +406 -0
- package/dist/core/agent/proxy/adapters/local-adapter.d.ts +2 -0
- package/dist/core/agent/proxy/adapters/local-adapter.js +93 -0
- package/dist/core/agent/proxy/adapters/openclawx-adapter.d.ts +2 -0
- package/dist/core/agent/proxy/adapters/openclawx-adapter.js +115 -0
- package/dist/core/agent/proxy/adapters/opencode-adapter.d.ts +11 -0
- package/dist/core/agent/proxy/adapters/opencode-adapter.js +750 -0
- package/dist/core/agent/proxy/adapters/opencode-free-models.d.ts +20 -0
- package/dist/core/agent/proxy/adapters/opencode-free-models.js +14 -0
- package/dist/core/agent/proxy/adapters/opencode-local-runner.d.ts +5 -0
- package/dist/core/agent/proxy/adapters/opencode-local-runner.js +86 -0
- package/dist/core/agent/proxy/index.d.ts +3 -0
- package/dist/core/agent/proxy/index.js +16 -0
- package/dist/core/agent/proxy/registry.d.ts +7 -0
- package/dist/core/agent/proxy/registry.js +13 -0
- package/dist/core/agent/proxy/run-for-channel.d.ts +3 -0
- package/dist/core/agent/proxy/run-for-channel.js +31 -0
- package/dist/core/agent/proxy/types.d.ts +30 -0
- package/dist/core/agent/proxy/types.js +1 -0
- package/dist/core/agent/run.js +1 -1
- package/dist/core/config/agent-reload-pending.d.ts +9 -0
- package/dist/core/config/agent-reload-pending.js +66 -0
- package/dist/core/config/desktop-config.d.ts +111 -3
- package/dist/core/config/desktop-config.js +289 -26
- package/dist/core/inbound-message-preprocess.d.ts +27 -0
- package/dist/core/inbound-message-preprocess.js +96 -0
- package/dist/core/mcp/client.d.ts +4 -0
- package/dist/core/mcp/client.js +2 -0
- package/dist/core/mcp/config.d.ts +14 -3
- package/dist/core/mcp/config.js +68 -3
- package/dist/core/mcp/index.d.ts +8 -6
- package/dist/core/mcp/index.js +6 -3
- package/dist/core/mcp/operator.d.ts +17 -2
- package/dist/core/mcp/operator.js +97 -30
- package/dist/core/mcp/transport/index.d.ts +4 -0
- package/dist/core/mcp/transport/index.js +6 -1
- package/dist/core/mcp/transport/stdio.d.ts +6 -0
- package/dist/core/mcp/transport/stdio.js +22 -1
- package/dist/core/mcp/types.d.ts +18 -0
- package/dist/core/memory/compaction-extension.d.ts +4 -3
- package/dist/core/memory/compaction-extension.js +6 -14
- package/dist/core/memory/embedding-types.d.ts +10 -0
- package/dist/core/memory/embedding-types.js +5 -0
- package/dist/core/memory/embedding.d.ts +2 -1
- package/dist/core/memory/embedding.js +38 -6
- package/dist/core/memory/index.js +3 -0
- package/dist/core/memory/local-embedding-llama.d.ts +13 -0
- package/dist/core/memory/local-embedding-llama.js +76 -0
- package/dist/core/memory/local-embedding.d.ts +10 -0
- package/dist/core/memory/local-embedding.js +29 -0
- package/dist/core/memory/persist-compaction-on-close.d.ts +14 -0
- package/dist/core/memory/persist-compaction-on-close.js +32 -0
- package/dist/core/session-outlet/index.d.ts +19 -0
- package/dist/core/session-outlet/index.js +33 -0
- package/dist/core/session-outlet/outlet.d.ts +15 -0
- package/dist/core/session-outlet/outlet.js +49 -0
- package/dist/core/session-outlet/types.d.ts +35 -0
- package/dist/core/session-outlet/types.js +5 -0
- package/dist/core/tools/bookmark-tool.d.ts +4 -0
- package/dist/core/tools/bookmark-tool.js +59 -3
- package/dist/core/tools/index.d.ts +2 -1
- package/dist/core/tools/index.js +2 -1
- package/dist/core/tools/memory-recall-tool.d.ts +6 -0
- package/dist/core/tools/memory-recall-tool.js +77 -0
- package/dist/gateway/channel/adapters/telegram.js +13 -2
- package/dist/gateway/channel/adapters/wechat.d.ts +24 -0
- package/dist/gateway/channel/adapters/wechat.js +205 -0
- package/dist/gateway/channel/channel-core.d.ts +1 -0
- package/dist/gateway/channel/channel-core.js +101 -59
- package/dist/gateway/channel/run-agent.d.ts +2 -4
- package/dist/gateway/channel/run-agent.js +13 -125
- package/dist/gateway/methods/agent-cancel.d.ts +3 -1
- package/dist/gateway/methods/agent-cancel.js +16 -2
- package/dist/gateway/methods/agent-chat.d.ts +4 -0
- package/dist/gateway/methods/agent-chat.js +309 -118
- package/dist/gateway/methods/run-scheduled-task.js +7 -7
- package/dist/gateway/proxy-run-abort.d.ts +6 -0
- package/dist/gateway/proxy-run-abort.js +39 -0
- package/dist/gateway/server.js +64 -7
- package/dist/server/agent-config/agent-config.controller.d.ts +2 -2
- package/dist/server/agent-config/agent-config.controller.js +8 -4
- package/dist/server/agent-config/agent-config.module.js +3 -1
- package/dist/server/agent-config/agent-config.service.d.ts +67 -6
- package/dist/server/agent-config/agent-config.service.js +75 -3
- package/dist/server/agents/agents.controller.d.ts +16 -0
- package/dist/server/agents/agents.controller.js +62 -1
- package/dist/server/agents/agents.service.js +1 -1
- package/dist/server/bootstrap.js +9 -2
- package/dist/server/config/config.controller.d.ts +31 -2
- package/dist/server/config/config.controller.js +14 -0
- package/dist/server/config/config.module.js +2 -2
- package/dist/server/config/config.service.d.ts +14 -1
- package/dist/server/config/config.service.js +1 -0
- package/dist/server/workspace/workspace.service.d.ts +7 -0
- package/dist/server/workspace/workspace.service.js +16 -0
- package/package.json +8 -2
- package/presets/preset-agents.json +94 -0
- package/presets/preset-config.json +11 -0
- package/presets/preset-providers.json +173 -0
- package/presets/workspaces/code-assistant/skills/code-review/SKILL.md +19 -0
- package/presets/workspaces/code-assistant/skills/code-runner/SKILL.md +21 -0
- package/presets/workspaces/code-assistant/skills/git-helper/SKILL.md +29 -0
- package/presets/workspaces/creator-assistant/skills/.gitkeep +0 -0
- package/presets/workspaces/creator-assistant/skills/creator-tools/SKILL.md +15 -0
- package/presets/workspaces/doc-assistant/skills/doc-processor/SKILL.md +21 -0
- package/presets/workspaces/download-assistant/skills/downloader/SKILL.md +20 -0
- package/presets/workspaces/file-assistant/skills/file-converter/SKILL.md +21 -0
- package/presets/workspaces/file-assistant/skills/file-organizer/SKILL.md +17 -0
- package/presets/workspaces/file-assistant/skills/file-search/SKILL.md +22 -0
- package/presets/workspaces/morning-briefing/skills/news-fetcher/SKILL.md +16 -0
- package/presets/workspaces/morning-briefing/skills/web-summarizer/SKILL.md +20 -0
- package/presets/workspaces/news-assistant/skills/news-fetcher/SKILL.md +16 -0
- package/presets/workspaces/news-assistant/skills/web-summarizer/SKILL.md +20 -0
- package/apps/desktop/renderer/dist/assets/index-LCp1YPVA.css +0 -10
- package/apps/desktop/renderer/dist/assets/index-l5fpDsHs.js +0 -89
package/dist/core/mcp/index.d.ts
CHANGED
|
@@ -3,16 +3,18 @@
|
|
|
3
3
|
* 配置在创建 Session 时通过 options.mcpServers 传入,与 Skill 类似。
|
|
4
4
|
*/
|
|
5
5
|
import type { ToolDefinition } from "@mariozechner/pi-coding-agent";
|
|
6
|
-
import type { McpServerConfig } from "./types.js";
|
|
7
|
-
export type { McpServerConfig, McpServerConfigStdio, McpServerConfigSse, McpTool } from "./types.js";
|
|
8
|
-
export { resolveMcpServersForSession, stdioConfigKey, sseConfigKey, mcpConfigKey } from "./config.js";
|
|
6
|
+
import type { McpServerConfig, McpServersStandardFormat } from "./types.js";
|
|
7
|
+
export type { McpServerConfig, McpServerConfigStdio, McpServerConfigSse, McpServerConfigStandardEntry, McpServersStandardFormat, McpTool, } from "./types.js";
|
|
8
|
+
export { resolveMcpServersForSession, standardFormatToArray, arrayToStandardFormat, stdioConfigKey, sseConfigKey, mcpConfigKey, } from "./config.js";
|
|
9
9
|
export { McpClient } from "./client.js";
|
|
10
|
-
export { getMcpToolDefinitions, shutdownMcpClients } from "./operator.js";
|
|
10
|
+
export { getMcpToolDefinitions, shutdownMcpClients, type GetMcpToolDefinitionsOptions } from "./operator.js";
|
|
11
11
|
export { mcpToolToToolDefinition, mcpToolsToToolDefinitions } from "./adapter.js";
|
|
12
12
|
/**
|
|
13
13
|
* 根据会话选项中的 mcpServers 配置,返回该会话可用的 MCP 工具(ToolDefinition 数组)。
|
|
14
|
-
*
|
|
14
|
+
* 支持数组或标准 JSON 对象格式;在 AgentManager.getOrCreateSession 中调用,并入 customTools。
|
|
15
|
+
* 若提供 sessionId,MCP 连接/重试进度将经全局 sendSessionMessage 以系统消息推送。
|
|
15
16
|
*/
|
|
16
17
|
export declare function createMcpToolsForSession(options: {
|
|
17
|
-
mcpServers?: McpServerConfig[];
|
|
18
|
+
mcpServers?: McpServerConfig[] | McpServersStandardFormat;
|
|
19
|
+
sessionId?: string;
|
|
18
20
|
}): Promise<ToolDefinition[]>;
|
package/dist/core/mcp/index.js
CHANGED
|
@@ -4,17 +4,20 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { resolveMcpServersForSession } from "./config.js";
|
|
6
6
|
import { getMcpToolDefinitions } from "./operator.js";
|
|
7
|
-
export { resolveMcpServersForSession, stdioConfigKey, sseConfigKey, mcpConfigKey } from "./config.js";
|
|
7
|
+
export { resolveMcpServersForSession, standardFormatToArray, arrayToStandardFormat, stdioConfigKey, sseConfigKey, mcpConfigKey, } from "./config.js";
|
|
8
8
|
export { McpClient } from "./client.js";
|
|
9
9
|
export { getMcpToolDefinitions, shutdownMcpClients } from "./operator.js";
|
|
10
10
|
export { mcpToolToToolDefinition, mcpToolsToToolDefinitions } from "./adapter.js";
|
|
11
11
|
/**
|
|
12
12
|
* 根据会话选项中的 mcpServers 配置,返回该会话可用的 MCP 工具(ToolDefinition 数组)。
|
|
13
|
-
*
|
|
13
|
+
* 支持数组或标准 JSON 对象格式;在 AgentManager.getOrCreateSession 中调用,并入 customTools。
|
|
14
|
+
* 若提供 sessionId,MCP 连接/重试进度将经全局 sendSessionMessage 以系统消息推送。
|
|
14
15
|
*/
|
|
15
16
|
export async function createMcpToolsForSession(options) {
|
|
16
17
|
const configs = resolveMcpServersForSession(options.mcpServers);
|
|
17
18
|
if (configs.length === 0)
|
|
18
19
|
return [];
|
|
19
|
-
return getMcpToolDefinitions(configs
|
|
20
|
+
return getMcpToolDefinitions(configs, {
|
|
21
|
+
sessionId: options.sessionId,
|
|
22
|
+
});
|
|
20
23
|
}
|
|
@@ -4,11 +4,26 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { McpServerConfig } from "./types.js";
|
|
6
6
|
import type { ToolDefinition } from "@mariozechner/pi-coding-agent";
|
|
7
|
+
/** getMcpToolDefinitions 的可选参数:超时、重试与 sessionId(用于经 sendSessionMessage 推送进度) */
|
|
8
|
+
export interface GetMcpToolDefinitionsOptions {
|
|
9
|
+
/** 连接或 list_tools 失败时的重试次数(不含首次,默认 1) */
|
|
10
|
+
connectRetries?: number;
|
|
11
|
+
/** 重试间隔(毫秒,默认 3000) */
|
|
12
|
+
retryDelayMs?: number;
|
|
13
|
+
/** stdio 初始化超时(毫秒) */
|
|
14
|
+
initTimeoutMs?: number;
|
|
15
|
+
/** stdio 初始化重试次数(默认 1) */
|
|
16
|
+
initRetries?: number;
|
|
17
|
+
/** stdio 初始化重试间隔(毫秒,默认 3000) */
|
|
18
|
+
initRetryDelayMs?: number;
|
|
19
|
+
/** 会话 ID,用于经全局 sendSessionMessage 推送 MCP 进度系统消息 */
|
|
20
|
+
sessionId?: string;
|
|
21
|
+
}
|
|
7
22
|
/**
|
|
8
23
|
* 为给定 MCP 服务器配置列表获取或创建客户端,并返回其工具对应的 ToolDefinition 数组。
|
|
9
|
-
* 连接失败或 list_tools 失败的 server
|
|
24
|
+
* 连接失败或 list_tools 失败的 server 会按 options 重试,仍失败则跳过并打日志,不阻塞整体。
|
|
10
25
|
*/
|
|
11
|
-
export declare function getMcpToolDefinitions(serverConfigs: McpServerConfig[]): Promise<ToolDefinition[]>;
|
|
26
|
+
export declare function getMcpToolDefinitions(serverConfigs: McpServerConfig[], options?: GetMcpToolDefinitionsOptions): Promise<ToolDefinition[]>;
|
|
12
27
|
/**
|
|
13
28
|
* 关闭并移除所有缓存的 MCP 客户端(进程退出或显式清理时调用)。
|
|
14
29
|
*/
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { mcpConfigKey } from "./config.js";
|
|
6
6
|
import { McpClient } from "./client.js";
|
|
7
7
|
import { mcpToolsToToolDefinitions } from "./adapter.js";
|
|
8
|
+
import { sendSessionMessage } from "../session-outlet/index.js";
|
|
8
9
|
/** 按配置键缓存的客户端 */
|
|
9
10
|
const clientCache = new Map();
|
|
10
11
|
function configLabel(config) {
|
|
@@ -12,53 +13,119 @@ function configLabel(config) {
|
|
|
12
13
|
return config.command;
|
|
13
14
|
return config.url;
|
|
14
15
|
}
|
|
16
|
+
/** 用于系统消息展示的 MCP 名称:stdio 优先用首参(如 akshare-tools),否则 command;sse 用 URL 主机或路径末段 */
|
|
17
|
+
function mcpDisplayName(config) {
|
|
18
|
+
if (config.transport === "stdio") {
|
|
19
|
+
const args = config.args;
|
|
20
|
+
const first = args?.[0];
|
|
21
|
+
if (typeof first === "string" && first.trim() && !first.includes("/") && !first.includes("\\")) {
|
|
22
|
+
return first.trim();
|
|
23
|
+
}
|
|
24
|
+
return config.command;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const u = new URL(config.url);
|
|
28
|
+
const host = u.hostname || u.pathname?.replace(/\/$/, "").split("/").pop() || "MCP";
|
|
29
|
+
return host;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return config.url;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function sleep(ms) {
|
|
36
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
37
|
+
}
|
|
15
38
|
/**
|
|
16
39
|
* 为给定 MCP 服务器配置列表获取或创建客户端,并返回其工具对应的 ToolDefinition 数组。
|
|
17
|
-
* 连接失败或 list_tools 失败的 server
|
|
40
|
+
* 连接失败或 list_tools 失败的 server 会按 options 重试,仍失败则跳过并打日志,不阻塞整体。
|
|
18
41
|
*/
|
|
19
|
-
export async function getMcpToolDefinitions(serverConfigs) {
|
|
42
|
+
export async function getMcpToolDefinitions(serverConfigs, options = {}) {
|
|
43
|
+
const connectRetries = options.connectRetries ?? 1;
|
|
44
|
+
const retryDelayMs = options.retryDelayMs ?? 3_000;
|
|
45
|
+
const sessionId = options.sessionId;
|
|
46
|
+
const clientOptions = {
|
|
47
|
+
initTimeoutMs: options.initTimeoutMs,
|
|
48
|
+
initRetries: options.initRetries,
|
|
49
|
+
initRetryDelayMs: options.initRetryDelayMs,
|
|
50
|
+
};
|
|
51
|
+
const emitProgress = (displayMessage, phase, detail) => {
|
|
52
|
+
if (sessionId) {
|
|
53
|
+
sendSessionMessage(sessionId, {
|
|
54
|
+
type: "system",
|
|
55
|
+
code: "mcp.progress",
|
|
56
|
+
payload: { phase, message: displayMessage, serverLabel: detail },
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
};
|
|
20
60
|
const allTools = [];
|
|
21
61
|
for (let i = 0; i < serverConfigs.length; i++) {
|
|
22
62
|
const config = serverConfigs[i];
|
|
23
63
|
const key = mcpConfigKey(config);
|
|
64
|
+
const label = configLabel(config);
|
|
65
|
+
const displayName = mcpDisplayName(config);
|
|
24
66
|
let client = clientCache.get(key);
|
|
25
|
-
if (!client) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
67
|
+
if (!client || !client.isConnected) {
|
|
68
|
+
if (client) {
|
|
69
|
+
clientCache.delete(key);
|
|
70
|
+
try {
|
|
71
|
+
await client.close();
|
|
72
|
+
}
|
|
73
|
+
catch { }
|
|
30
74
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
75
|
+
emitProgress(`${displayName} MCP connecting`, "connecting");
|
|
76
|
+
let connected = false;
|
|
77
|
+
for (let attempt = 0; attempt <= connectRetries; attempt++) {
|
|
78
|
+
if (attempt > 0) {
|
|
79
|
+
emitProgress(`${displayName} MCP retrying (${attempt + 1}/${connectRetries + 1})`, "retrying", "connect");
|
|
80
|
+
await sleep(retryDelayMs);
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
client = new McpClient(config, clientOptions);
|
|
84
|
+
await client.connect();
|
|
85
|
+
clientCache.set(key, client);
|
|
86
|
+
connected = true;
|
|
87
|
+
emitProgress(`${displayName} MCP ready`, "ready");
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
if (client)
|
|
92
|
+
try {
|
|
93
|
+
await client.close();
|
|
94
|
+
}
|
|
95
|
+
catch { }
|
|
96
|
+
if (attempt === connectRetries) {
|
|
97
|
+
console.warn(`[mcp] 连接失败 (${label}):`, err instanceof Error ? err.message : err);
|
|
98
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
99
|
+
emitProgress(`${displayName} MCP failed: ${errMsg}`, "skipped", errMsg);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
34
102
|
}
|
|
103
|
+
if (!connected)
|
|
104
|
+
continue;
|
|
35
105
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
106
|
+
client = clientCache.get(key);
|
|
107
|
+
let toolsListed = false;
|
|
108
|
+
for (let attempt = 0; attempt <= connectRetries; attempt++) {
|
|
109
|
+
if (attempt > 0) {
|
|
110
|
+
emitProgress(`${displayName} MCP retrying list tools (${attempt + 1}/${connectRetries + 1})`, "retrying", "list_tools");
|
|
111
|
+
await sleep(retryDelayMs);
|
|
40
112
|
}
|
|
41
|
-
catch { }
|
|
42
|
-
const newClient = new McpClient(config);
|
|
43
113
|
try {
|
|
44
|
-
await
|
|
45
|
-
|
|
46
|
-
|
|
114
|
+
const tools = await client.listTools();
|
|
115
|
+
const serverId = `mcp${i}`;
|
|
116
|
+
const definitions = mcpToolsToToolDefinitions(tools, client, serverId);
|
|
117
|
+
allTools.push(...definitions);
|
|
118
|
+
toolsListed = true;
|
|
119
|
+
break;
|
|
47
120
|
}
|
|
48
121
|
catch (err) {
|
|
49
|
-
|
|
50
|
-
|
|
122
|
+
if (attempt === connectRetries) {
|
|
123
|
+
console.warn(`[mcp] list_tools 失败 (${label}):`, err instanceof Error ? err.message : err);
|
|
124
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
125
|
+
emitProgress(`${displayName} MCP list tools failed: ${errMsg}`, "skipped", errMsg);
|
|
126
|
+
}
|
|
51
127
|
}
|
|
52
128
|
}
|
|
53
|
-
try {
|
|
54
|
-
const tools = await client.listTools();
|
|
55
|
-
const serverId = `mcp${i}`;
|
|
56
|
-
const definitions = mcpToolsToToolDefinitions(tools, client, serverId);
|
|
57
|
-
allTools.push(...definitions);
|
|
58
|
-
}
|
|
59
|
-
catch (err) {
|
|
60
|
-
console.warn(`[mcp] list_tools 失败 (${configLabel(config)}):`, err instanceof Error ? err.message : err);
|
|
61
|
-
}
|
|
62
129
|
}
|
|
63
130
|
return allTools;
|
|
64
131
|
}
|
|
@@ -2,6 +2,10 @@ import type { McpServerConfig, IMcpTransport } from "../types.js";
|
|
|
2
2
|
export interface TransportOptions {
|
|
3
3
|
initTimeoutMs?: number;
|
|
4
4
|
requestTimeoutMs?: number;
|
|
5
|
+
/** stdio:初始化失败时的重试次数(默认 1) */
|
|
6
|
+
initRetries?: number;
|
|
7
|
+
/** stdio:初始化重试间隔毫秒(默认 3000) */
|
|
8
|
+
initRetryDelayMs?: number;
|
|
5
9
|
}
|
|
6
10
|
/**
|
|
7
11
|
* 根据配置创建对应的传输层实例。
|
|
@@ -5,7 +5,12 @@ import { SseTransport } from "./sse.js";
|
|
|
5
5
|
*/
|
|
6
6
|
export function createTransport(config, options) {
|
|
7
7
|
if (config.transport === "stdio") {
|
|
8
|
-
return new StdioTransport(config,
|
|
8
|
+
return new StdioTransport(config, {
|
|
9
|
+
initTimeoutMs: options?.initTimeoutMs,
|
|
10
|
+
requestTimeoutMs: options?.requestTimeoutMs,
|
|
11
|
+
initRetries: options?.initRetries,
|
|
12
|
+
initRetryDelayMs: options?.initRetryDelayMs,
|
|
13
|
+
});
|
|
9
14
|
}
|
|
10
15
|
if (config.transport === "sse") {
|
|
11
16
|
return new SseTransport(config, options);
|
|
@@ -8,12 +8,18 @@ export interface StdioTransportOptions {
|
|
|
8
8
|
initTimeoutMs?: number;
|
|
9
9
|
/** 单次请求超时(毫秒) */
|
|
10
10
|
requestTimeoutMs?: number;
|
|
11
|
+
/** 初始化失败时的重试次数(不含首次,默认 1,即最多 2 次尝试) */
|
|
12
|
+
initRetries?: number;
|
|
13
|
+
/** 初始化重试间隔(毫秒,默认 3000) */
|
|
14
|
+
initRetryDelayMs?: number;
|
|
11
15
|
}
|
|
12
16
|
export declare class StdioTransport {
|
|
13
17
|
private process;
|
|
14
18
|
private config;
|
|
15
19
|
private initTimeoutMs;
|
|
16
20
|
private requestTimeoutMs;
|
|
21
|
+
private initRetries;
|
|
22
|
+
private initRetryDelayMs;
|
|
17
23
|
private nextId;
|
|
18
24
|
private pending;
|
|
19
25
|
private buffer;
|
|
@@ -8,6 +8,8 @@ export class StdioTransport {
|
|
|
8
8
|
config;
|
|
9
9
|
initTimeoutMs;
|
|
10
10
|
requestTimeoutMs;
|
|
11
|
+
initRetries;
|
|
12
|
+
initRetryDelayMs;
|
|
11
13
|
nextId = 1;
|
|
12
14
|
pending = new Map();
|
|
13
15
|
buffer = "";
|
|
@@ -15,6 +17,8 @@ export class StdioTransport {
|
|
|
15
17
|
this.config = config;
|
|
16
18
|
this.initTimeoutMs = options.initTimeoutMs ?? 10_000;
|
|
17
19
|
this.requestTimeoutMs = options.requestTimeoutMs ?? 30_000;
|
|
20
|
+
this.initRetries = options.initRetries ?? 1;
|
|
21
|
+
this.initRetryDelayMs = options.initRetryDelayMs ?? 3_000;
|
|
18
22
|
}
|
|
19
23
|
/** 启动子进程并完成 MCP initialize 握手 */
|
|
20
24
|
async start() {
|
|
@@ -43,7 +47,24 @@ export class StdioTransport {
|
|
|
43
47
|
this.rejectAll(new Error(`MCP process exited: code=${code} signal=${signal}`));
|
|
44
48
|
this.process = null;
|
|
45
49
|
});
|
|
46
|
-
|
|
50
|
+
const maxAttempts = 1 + this.initRetries;
|
|
51
|
+
let lastErr = null;
|
|
52
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
53
|
+
try {
|
|
54
|
+
await this.initialize();
|
|
55
|
+
lastErr = null;
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
lastErr = e instanceof Error ? e : new Error(String(e));
|
|
60
|
+
const isTransient = lastErr.message.includes("timeout") || lastErr.message.includes("MCP process exited");
|
|
61
|
+
if (!isTransient || attempt >= maxAttempts) {
|
|
62
|
+
throw lastErr;
|
|
63
|
+
}
|
|
64
|
+
console.warn(`[mcp stdio] initialize 超时或失败,${this.initRetryDelayMs}ms 后重试 (${attempt}/${maxAttempts}):`, lastErr.message);
|
|
65
|
+
await new Promise((r) => setTimeout(r, this.initRetryDelayMs));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
47
68
|
}
|
|
48
69
|
flushLines() {
|
|
49
70
|
const lines = this.buffer.split("\n");
|
package/dist/core/mcp/types.d.ts
CHANGED
|
@@ -22,6 +22,24 @@ export interface McpServerConfigSse {
|
|
|
22
22
|
}
|
|
23
23
|
/** 单条 MCP 服务器配置(由调用方在创建 Session 时传入,与 Skill 类似) */
|
|
24
24
|
export type McpServerConfig = McpServerConfigStdio | McpServerConfigSse;
|
|
25
|
+
/**
|
|
26
|
+
* 标准 JSON 格式中的单条 MCP 配置(无 transport,由 command/url 推断)。
|
|
27
|
+
* 与 Claude/Anthropic 等生态常用格式一致。
|
|
28
|
+
*/
|
|
29
|
+
export interface McpServerConfigStandardEntry {
|
|
30
|
+
/** stdio:可执行命令 */
|
|
31
|
+
command?: string;
|
|
32
|
+
/** stdio:命令行参数 */
|
|
33
|
+
args?: string[];
|
|
34
|
+
/** stdio:环境变量 */
|
|
35
|
+
env?: Record<string, string>;
|
|
36
|
+
/** sse:远程服务地址 */
|
|
37
|
+
url?: string;
|
|
38
|
+
/** sse:请求头 */
|
|
39
|
+
headers?: Record<string, string>;
|
|
40
|
+
}
|
|
41
|
+
/** 标准 JSON 格式:mcpServers 为对象,key 为服务器名称 */
|
|
42
|
+
export type McpServersStandardFormat = Record<string, McpServerConfigStandardEntry>;
|
|
25
43
|
/** MCP 协议中 Tool 的 inputSchema 为 JSON Schema 对象 */
|
|
26
44
|
export interface McpToolInputSchema {
|
|
27
45
|
type?: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ExtensionFactory } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
/**
|
|
3
|
-
* 创建用于在 session_compact
|
|
4
|
-
*
|
|
3
|
+
* 创建用于在 session_compact 事件时更新「当前 session 最新 compaction」的 extension factory。
|
|
4
|
+
* 发生 compaction 时只回调 onUpdateLatestCompaction(summary),由调用方保存;
|
|
5
|
+
* 向量库写入推迟到 SessionAgent 关闭时由 AgentManager 统一执行。
|
|
5
6
|
*/
|
|
6
|
-
export declare function createCompactionMemoryExtensionFactory(
|
|
7
|
+
export declare function createCompactionMemoryExtensionFactory(_sessionId: string, onUpdateLatestCompaction: (summary: string) => void): ExtensionFactory;
|
|
@@ -1,23 +1,15 @@
|
|
|
1
|
-
import { addMemory } from "./index.js";
|
|
2
1
|
/**
|
|
3
|
-
* 创建用于在 session_compact
|
|
4
|
-
*
|
|
2
|
+
* 创建用于在 session_compact 事件时更新「当前 session 最新 compaction」的 extension factory。
|
|
3
|
+
* 发生 compaction 时只回调 onUpdateLatestCompaction(summary),由调用方保存;
|
|
4
|
+
* 向量库写入推迟到 SessionAgent 关闭时由 AgentManager 统一执行。
|
|
5
5
|
*/
|
|
6
|
-
export function createCompactionMemoryExtensionFactory(
|
|
6
|
+
export function createCompactionMemoryExtensionFactory(_sessionId, onUpdateLatestCompaction) {
|
|
7
7
|
return (pi) => {
|
|
8
|
-
pi.on("session_compact",
|
|
8
|
+
pi.on("session_compact", (event) => {
|
|
9
9
|
const summary = event.compactionEntry?.summary?.trim();
|
|
10
10
|
if (!summary)
|
|
11
11
|
return;
|
|
12
|
-
|
|
13
|
-
await addMemory(summary, {
|
|
14
|
-
infotype: "compaction",
|
|
15
|
-
sessionId,
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
catch (_) {
|
|
19
|
-
// 写入失败不打断会话
|
|
20
|
-
}
|
|
12
|
+
onUpdateLatestCompaction(summary);
|
|
21
13
|
});
|
|
22
14
|
};
|
|
23
15
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedding 抽象:便于后续切换不同实现(在线 OpenAPI、node-llama-cpp GGUF 等)。
|
|
3
|
+
* 调用方仅依赖 embed(text) => Promise<number[] | null>。
|
|
4
|
+
*/
|
|
5
|
+
export interface IEmbeddingProvider {
|
|
6
|
+
/** 单条文本向量化,不可用时返回 null */
|
|
7
|
+
embed(text: string): Promise<number[] | null>;
|
|
8
|
+
/** 提供方名称,用于日志 */
|
|
9
|
+
readonly name: string;
|
|
10
|
+
}
|
|
@@ -1,15 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 文本 embedding
|
|
3
|
-
*
|
|
2
|
+
* 文本 embedding 统一入口(供 memory 向量库等使用)。
|
|
3
|
+
*
|
|
4
|
+
* 使用逻辑:
|
|
5
|
+
* - 未配置在线 embedding:直接使用本地 embedding(GGUF)。
|
|
6
|
+
* - 已配置在线 embedding:先尝试在线;若请求失败(无法连接、超时、返回错误等),则回退到本地 embedding。
|
|
7
|
+
* - 本地也不可用时返回 null,长记忆空转。
|
|
4
8
|
*/
|
|
5
9
|
import { getRagEmbeddingConfigSync } from "../config/desktop-config.js";
|
|
6
10
|
import { embedRemote } from "./remote-embedding.js";
|
|
11
|
+
import { getLocalEmbeddingProvider, getLocalEmbeddingUnavailableReason } from "./local-embedding.js";
|
|
12
|
+
let fallbackLogged = false;
|
|
13
|
+
let unavailableLogged = false;
|
|
7
14
|
/**
|
|
8
|
-
* 对单条文本做 embedding
|
|
15
|
+
* 对单条文本做 embedding。
|
|
16
|
+
* 未配置在线或在线连接失败时使用本地 embedding;均不可用时返回 null(仅首次打日志)。
|
|
9
17
|
*/
|
|
10
18
|
export async function embed(text) {
|
|
11
19
|
const config = getRagEmbeddingConfigSync();
|
|
12
|
-
if (
|
|
13
|
-
|
|
14
|
-
|
|
20
|
+
if (config) {
|
|
21
|
+
try {
|
|
22
|
+
const vec = await embedRemote(text, config);
|
|
23
|
+
if (Array.isArray(vec) && vec.length > 0)
|
|
24
|
+
return vec;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// 配置了在线但请求失败(无法连接等),下面使用本地
|
|
28
|
+
}
|
|
29
|
+
if (!fallbackLogged) {
|
|
30
|
+
fallbackLogged = true;
|
|
31
|
+
console.warn("[RAG embedding] 在线模型不可用,已切换至本地模型(GGUF)");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const local = await getLocalEmbeddingProvider();
|
|
35
|
+
if (local) {
|
|
36
|
+
const vec = await local.embed(text);
|
|
37
|
+
if (Array.isArray(vec) && vec.length > 0)
|
|
38
|
+
return vec;
|
|
39
|
+
}
|
|
40
|
+
if (!unavailableLogged) {
|
|
41
|
+
unavailableLogged = true;
|
|
42
|
+
const reason = getLocalEmbeddingUnavailableReason();
|
|
43
|
+
const reasonSuffix = reason ? ` 原因: ${reason}` : "(若为首次加载,请先执行 npm run build 后重启桌面端以查看具体原因)";
|
|
44
|
+
console.warn("[RAG embedding] 本地模型也不可用,长记忆将空转。" + reasonSuffix);
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
15
47
|
}
|
|
@@ -26,6 +26,9 @@ export async function addMemory(text, metadata) {
|
|
|
26
26
|
createdAt: new Date().toISOString(),
|
|
27
27
|
};
|
|
28
28
|
await addToStore(id, vec, text, meta);
|
|
29
|
+
if (metadata.infotype === "experience") {
|
|
30
|
+
console.log(`[Memory] 经验已写入向量库 sessionId=${metadata.sessionId} id=${id}`);
|
|
31
|
+
}
|
|
29
32
|
return id;
|
|
30
33
|
}
|
|
31
34
|
/**
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 本地 embedding(Electron 主进程):node-llama-cpp + GGUF。
|
|
3
|
+
* 需在安装依赖后对 Electron 的 Node ABI 执行 electron-rebuild(见 apps/desktop 的 rebuild:native)。
|
|
4
|
+
* 加载前会注册 ESM 钩子,将 "typescript" 解析为桩模块,避免依赖链加载 typescript.js(含 with 语句,ESM 下报错)。
|
|
5
|
+
*/
|
|
6
|
+
import type { IEmbeddingProvider } from "./embedding-types.js";
|
|
7
|
+
export declare function getLocalEmbeddingLlamaUnavailableReason(): string | null;
|
|
8
|
+
/**
|
|
9
|
+
* 获取基于 node-llama-cpp + GGUF 的本地 embedding 提供方。
|
|
10
|
+
* 仅在可解析到 node-llama-cpp 且模型可加载时返回,否则返回 null。
|
|
11
|
+
* @param modelPath 配置的 GGUF 路径或 hf: URI,为空时使用 DEFAULT_GGUF_MODEL
|
|
12
|
+
*/
|
|
13
|
+
export declare function getLocalEmbeddingLlamaProvider(modelPath: string | null): Promise<IEmbeddingProvider | null>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { join } from "path";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
let cached = null;
|
|
4
|
+
let initError = null;
|
|
5
|
+
let lastQueryError = null;
|
|
6
|
+
export function getLocalEmbeddingLlamaUnavailableReason() {
|
|
7
|
+
return initError?.message ?? lastQueryError ?? null;
|
|
8
|
+
}
|
|
9
|
+
function L2Normalize(vec) {
|
|
10
|
+
let sum = 0;
|
|
11
|
+
for (let i = 0; i < vec.length; i++)
|
|
12
|
+
sum += vec[i] * vec[i];
|
|
13
|
+
const norm = Math.sqrt(sum) || 1;
|
|
14
|
+
return vec.map((x) => x / norm);
|
|
15
|
+
}
|
|
16
|
+
/** 默认 GGUF embedding 模型(与 moltbot 一致),首次使用会按 node-llama-cpp 规则下载到缓存 */
|
|
17
|
+
const DEFAULT_GGUF_MODEL = "hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf";
|
|
18
|
+
/**
|
|
19
|
+
* 获取基于 node-llama-cpp + GGUF 的本地 embedding 提供方。
|
|
20
|
+
* 仅在可解析到 node-llama-cpp 且模型可加载时返回,否则返回 null。
|
|
21
|
+
* @param modelPath 配置的 GGUF 路径或 hf: URI,为空时使用 DEFAULT_GGUF_MODEL
|
|
22
|
+
*/
|
|
23
|
+
export async function getLocalEmbeddingLlamaProvider(modelPath) {
|
|
24
|
+
if (cached)
|
|
25
|
+
return cached;
|
|
26
|
+
if (initError)
|
|
27
|
+
return null;
|
|
28
|
+
const effectivePath = (modelPath?.trim() || DEFAULT_GGUF_MODEL).trim();
|
|
29
|
+
try {
|
|
30
|
+
// Node 20.10+ 才提供 node:module 的 register;Electron 内置 Node 较旧时无此导出,跳过钩子
|
|
31
|
+
const nodeModule = await import("node:module");
|
|
32
|
+
if (typeof nodeModule.register === "function") {
|
|
33
|
+
const loaderUrl = new URL("../../../scripts/ts-stub-loader.mjs", import.meta.url).href;
|
|
34
|
+
await Promise.resolve(nodeModule.register(loaderUrl, import.meta.url));
|
|
35
|
+
}
|
|
36
|
+
const { getLlama, resolveModelFile, LlamaLogLevel } = await import("node-llama-cpp");
|
|
37
|
+
const llama = await getLlama({ logLevel: LlamaLogLevel.error });
|
|
38
|
+
const cacheDir = join(homedir(), ".cache", "llama");
|
|
39
|
+
const resolved = await resolveModelFile(effectivePath, cacheDir);
|
|
40
|
+
const model = await llama.loadModel({ modelPath: resolved });
|
|
41
|
+
const embeddingCtx = await model.createEmbeddingContext();
|
|
42
|
+
const provider = {
|
|
43
|
+
name: "llama-gguf",
|
|
44
|
+
async embed(text) {
|
|
45
|
+
try {
|
|
46
|
+
const embedding = await embeddingCtx.getEmbeddingFor(text);
|
|
47
|
+
const vec = Array.from(embedding.vector);
|
|
48
|
+
if (vec.length === 0) {
|
|
49
|
+
lastQueryError = "getEmbeddingFor 返回空向量";
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return L2Normalize(vec);
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
56
|
+
lastQueryError = `llama embed 失败: ${err.message}`;
|
|
57
|
+
console.warn("[RAG embedding] node-llama-cpp embed 失败:", err.message);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
cached = provider;
|
|
63
|
+
return provider;
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
initError = e instanceof Error ? e : new Error(String(e));
|
|
67
|
+
console.warn("[RAG embedding] node-llama-cpp (GGUF) 不可用,原因:", initError.message);
|
|
68
|
+
if (initError.stack) {
|
|
69
|
+
console.warn("[RAG embedding] GGUF 加载失败堆栈:\n", initError.stack);
|
|
70
|
+
}
|
|
71
|
+
if (initError.message.includes("Unexpected token 'with'")) {
|
|
72
|
+
console.warn("[RAG embedding] 提示: 依赖链中某包在 ESM 下使用了禁止的 with 语句(如 typescript.js)。可配置在线 RAG 或升级 Electron 至 Node 22+。");
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 本地 embedding:仅 node-llama-cpp (GGUF)。不可用时返回 null,由上层决定是否使用在线 RAG。
|
|
3
|
+
*/
|
|
4
|
+
import type { IEmbeddingProvider } from "./embedding-types.js";
|
|
5
|
+
export declare function getLocalEmbeddingUnavailableReason(): string | null;
|
|
6
|
+
/**
|
|
7
|
+
* 获取本地 embedding 提供方(懒加载,失败后不再重试)。
|
|
8
|
+
* 仅使用 node-llama-cpp (GGUF)。不可用时返回 null。
|
|
9
|
+
*/
|
|
10
|
+
export declare function getLocalEmbeddingProvider(): Promise<IEmbeddingProvider | null>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { getRagLocalModelPathSync } from "../config/desktop-config.js";
|
|
2
|
+
import { getLocalEmbeddingLlamaProvider, getLocalEmbeddingLlamaUnavailableReason, } from "./local-embedding-llama.js";
|
|
3
|
+
let cached = null;
|
|
4
|
+
let envLogged = false;
|
|
5
|
+
export function getLocalEmbeddingUnavailableReason() {
|
|
6
|
+
return getLocalEmbeddingLlamaUnavailableReason();
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* 获取本地 embedding 提供方(懒加载,失败后不再重试)。
|
|
10
|
+
* 仅使用 node-llama-cpp (GGUF)。不可用时返回 null。
|
|
11
|
+
*/
|
|
12
|
+
export async function getLocalEmbeddingProvider() {
|
|
13
|
+
if (cached)
|
|
14
|
+
return cached;
|
|
15
|
+
const provider = await getLocalEmbeddingLlamaProvider(getRagLocalModelPathSync());
|
|
16
|
+
if (provider) {
|
|
17
|
+
cached = provider;
|
|
18
|
+
if (!envLogged) {
|
|
19
|
+
envLogged = true;
|
|
20
|
+
console.warn("[RAG embedding] 本地模型使用 node-llama-cpp (GGUF)");
|
|
21
|
+
}
|
|
22
|
+
return cached;
|
|
23
|
+
}
|
|
24
|
+
if (!envLogged) {
|
|
25
|
+
envLogged = true;
|
|
26
|
+
console.warn("[RAG embedding] 本地模型不可用,长记忆将空转。可配置 RAG 在线模型或检查 GGUF 报错。");
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type AddMemoryCompaction = (text: string, meta: {
|
|
2
|
+
infotype: "compaction";
|
|
3
|
+
sessionId: string;
|
|
4
|
+
}) => Promise<unknown>;
|
|
5
|
+
/**
|
|
6
|
+
* 将 map 中指定 compositeKey 的 summary 写入 addMemory 并从 map 删除;用于 Session 关闭前。
|
|
7
|
+
* 写入采用 fire-and-forget,不阻塞调用方,避免 agent.chat 等请求被 addMemory/embed 拖住导致无回复。
|
|
8
|
+
*/
|
|
9
|
+
export declare function persistStoredCompactionForSession(sessionLatestCompactionSummary: Map<string, string>, compositeKey: string, addMemory: AddMemoryCompaction): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* 将 map 中所有给定 keys(需以 sessionId + "::" 为前缀)的 summary 依次写入 addMemory 并删除;用于按业务 sessionId 关闭时。
|
|
12
|
+
* 写入采用 fire-and-forget,不阻塞调用方。
|
|
13
|
+
*/
|
|
14
|
+
export declare function persistStoredCompactionForBusinessSession(sessionLatestCompactionSummary: Map<string, string>, keysToProcess: string[], sessionId: string, addMemory: AddMemoryCompaction): Promise<void>;
|