@next-open-ai/openclawx 0.8.36 → 0.8.40
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/README.md +50 -42
- package/apps/desktop/renderer/dist/assets/index-BSfTiTKo.css +10 -0
- package/apps/desktop/renderer/dist/assets/index-DgLpQsA-.js +89 -0
- package/apps/desktop/renderer/dist/index.html +2 -2
- package/dist/cli/cli.js +29 -0
- package/dist/cli/extension-cmd.d.ts +15 -0
- package/dist/cli/extension-cmd.js +107 -0
- package/dist/core/agent/agent-dir.d.ts +6 -0
- package/dist/core/agent/agent-dir.js +8 -0
- package/dist/core/agent/agent-manager.d.ts +13 -0
- package/dist/core/agent/agent-manager.js +68 -5
- package/dist/core/agent/proxy/adapters/claude-code-adapter.d.ts +2 -0
- package/dist/core/agent/proxy/adapters/claude-code-adapter.js +186 -0
- package/dist/core/agent/proxy/adapters/local-adapter.js +2 -0
- package/dist/core/agent/proxy/adapters/opencode-adapter.js +65 -29
- package/dist/core/agent/proxy/adapters/opencode-local-runner.js +9 -0
- package/dist/core/agent/proxy/index.js +2 -0
- package/dist/core/agent/token-usage-log-extension.d.ts +14 -0
- package/dist/core/agent/token-usage-log-extension.js +61 -0
- package/dist/core/config/desktop-config.d.ts +22 -2
- package/dist/core/config/desktop-config.js +57 -1
- package/dist/core/extensions/index.d.ts +1 -0
- package/dist/core/extensions/index.js +1 -0
- package/dist/core/extensions/load.d.ts +11 -0
- package/dist/core/extensions/load.js +101 -0
- package/dist/core/mcp/adapter.d.ts +4 -2
- package/dist/core/mcp/adapter.js +10 -4
- package/dist/core/mcp/index.d.ts +2 -0
- package/dist/core/mcp/index.js +1 -0
- package/dist/core/mcp/operator.d.ts +2 -0
- package/dist/core/mcp/operator.js +1 -1
- package/dist/core/tools/index.d.ts +1 -0
- package/dist/core/tools/index.js +1 -0
- package/dist/core/tools/truncate-result.d.ts +14 -0
- package/dist/core/tools/truncate-result.js +27 -0
- package/dist/core/tools/web-search/create-web-search-tool.d.ts +17 -0
- package/dist/core/tools/web-search/create-web-search-tool.js +87 -0
- package/dist/core/tools/web-search/index.d.ts +4 -0
- package/dist/core/tools/web-search/index.js +2 -0
- package/dist/core/tools/web-search/providers/brave.d.ts +2 -0
- package/dist/core/tools/web-search/providers/brave.js +87 -0
- package/dist/core/tools/web-search/providers/duck-duck-scrape.d.ts +2 -0
- package/dist/core/tools/web-search/providers/duck-duck-scrape.js +47 -0
- package/dist/core/tools/web-search/providers/index.d.ts +5 -0
- package/dist/core/tools/web-search/providers/index.js +13 -0
- package/dist/core/tools/web-search/types.d.ts +35 -0
- package/dist/core/tools/web-search/types.js +4 -0
- package/dist/gateway/methods/agent-chat.js +3 -1
- package/dist/gateway/methods/run-scheduled-task.js +2 -0
- package/dist/server/agent-config/agent-config.controller.d.ts +1 -1
- package/dist/server/agent-config/agent-config.service.d.ts +15 -3
- package/dist/server/agent-config/agent-config.service.js +18 -0
- package/dist/server/config/config.controller.d.ts +26 -0
- package/dist/server/config/config.service.d.ts +14 -0
- package/package.json +3 -1
- package/presets/preset-agents.json +121 -91
- package/presets/workspaces/finance-expert/skills/akshare-helper/SKILL.md +9 -0
- package/presets/workspaces/office-automation/skills/rpa-helper/SKILL.md +9 -0
- package/presets/workspaces/self-media-bot/skills/self-media-tools/SKILL.md +9 -0
- package/apps/desktop/renderer/dist/assets/index-BGHtXhm3.js +0 -89
- package/apps/desktop/renderer/dist/assets/index-CB2-m4ae.css +0 -10
|
@@ -113,7 +113,7 @@ export async function getMcpToolDefinitions(serverConfigs, options = {}) {
|
|
|
113
113
|
try {
|
|
114
114
|
const tools = await client.listTools();
|
|
115
115
|
const serverId = `mcp${i}`;
|
|
116
|
-
const definitions = mcpToolsToToolDefinitions(tools, client, serverId);
|
|
116
|
+
const definitions = mcpToolsToToolDefinitions(tools, client, serverId, options.maxResultTokens);
|
|
117
117
|
allTools.push(...definitions);
|
|
118
118
|
toolsListed = true;
|
|
119
119
|
break;
|
|
@@ -6,3 +6,4 @@ export { createSwitchAgentTool } from "./switch-agent-tool.js";
|
|
|
6
6
|
export { createListAgentsTool } from "./list-agents-tool.js";
|
|
7
7
|
export { createCreateAgentTool } from "./create-agent-tool.js";
|
|
8
8
|
export { createGetBookmarkTagsTool, createSaveBookmarkTool, createAddBookmarkTagTool, } from "./bookmark-tool.js";
|
|
9
|
+
export { createWebSearchTool } from "./web-search/index.js";
|
package/dist/core/tools/index.js
CHANGED
|
@@ -6,3 +6,4 @@ export { createSwitchAgentTool } from "./switch-agent-tool.js";
|
|
|
6
6
|
export { createListAgentsTool } from "./list-agents-tool.js";
|
|
7
7
|
export { createCreateAgentTool } from "./create-agent-tool.js";
|
|
8
8
|
export { createGetBookmarkTagsTool, createSaveBookmarkTool, createAddBookmarkTagTool, } from "./bookmark-tool.js";
|
|
9
|
+
export { createWebSearchTool } from "./web-search/index.js";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 工具返回结果按 token 估算截断:超过 maxTokens 时从尾部裁剪并打日志。
|
|
3
|
+
* 用于 MCP、web_search 等单次返回可能过大的场景。
|
|
4
|
+
*/
|
|
5
|
+
/** 粗略按字符估算 token(中英混合约 1/3) */
|
|
6
|
+
export declare function estTokensFromChars(chars: number): number;
|
|
7
|
+
/**
|
|
8
|
+
* 若 text 估算 token 超过 maxTokens,则保留前 maxTokens 对应字符并打日志,否则返回原 text。
|
|
9
|
+
* @param text 原始文本
|
|
10
|
+
* @param maxTokens 最大保留 token 数(仅当 > 0 时生效)
|
|
11
|
+
* @param toolLabel 工具标识,用于日志
|
|
12
|
+
* @returns 截断后的文本(可能为原 text)
|
|
13
|
+
*/
|
|
14
|
+
export declare function truncateTextToMaxTokens(text: string, maxTokens: number, toolLabel: string): string;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 工具返回结果按 token 估算截断:超过 maxTokens 时从尾部裁剪并打日志。
|
|
3
|
+
* 用于 MCP、web_search 等单次返回可能过大的场景。
|
|
4
|
+
*/
|
|
5
|
+
const LOG_PREFIX = "[tool-result]";
|
|
6
|
+
/** 粗略按字符估算 token(中英混合约 1/3) */
|
|
7
|
+
export function estTokensFromChars(chars) {
|
|
8
|
+
return Math.ceil(chars / 3);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 若 text 估算 token 超过 maxTokens,则保留前 maxTokens 对应字符并打日志,否则返回原 text。
|
|
12
|
+
* @param text 原始文本
|
|
13
|
+
* @param maxTokens 最大保留 token 数(仅当 > 0 时生效)
|
|
14
|
+
* @param toolLabel 工具标识,用于日志
|
|
15
|
+
* @returns 截断后的文本(可能为原 text)
|
|
16
|
+
*/
|
|
17
|
+
export function truncateTextToMaxTokens(text, maxTokens, toolLabel) {
|
|
18
|
+
if (!text || maxTokens <= 0)
|
|
19
|
+
return text;
|
|
20
|
+
const est = estTokensFromChars(text.length);
|
|
21
|
+
if (est <= maxTokens)
|
|
22
|
+
return text;
|
|
23
|
+
const keepChars = Math.max(1, maxTokens * 3);
|
|
24
|
+
const truncated = text.slice(0, keepChars);
|
|
25
|
+
console.log(`${LOG_PREFIX} ${toolLabel} 返回超限 estTokens=${est} maxTokens=${maxTokens} 已从尾部裁剪,保留约 ${maxTokens} token`);
|
|
26
|
+
return truncated;
|
|
27
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ToolDefinition } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
export interface WebSearchToolOptions {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
provider: "brave" | "duck-duck-scrape";
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
timeoutSeconds?: number;
|
|
7
|
+
cacheTtlMinutes?: number;
|
|
8
|
+
maxResults?: number;
|
|
9
|
+
/** 单次搜索返回内容最大 token;超过则从尾部裁剪并打日志;不配置则不限制 */
|
|
10
|
+
maxResultTokens?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 创建 web_search 工具。
|
|
14
|
+
* - enabled 为 false 时返回 null,调用方不应将工具加入 customTools。
|
|
15
|
+
* - enabled 为 true 但当前 provider 不可用(如 Brave 无 Key)时仍返回工具,执行时空转,返回固定提示。
|
|
16
|
+
*/
|
|
17
|
+
export declare function createWebSearchTool(options: WebSearchToolOptions): ToolDefinition | null;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { getWebSearchProvider } from "./providers/index.js";
|
|
3
|
+
import { truncateTextToMaxTokens } from "../truncate-result.js";
|
|
4
|
+
const WebSearchSchema = Type.Object({
|
|
5
|
+
query: Type.String({
|
|
6
|
+
description: "搜索关键词或问题,例如:当前天气、最新新闻、某概念解释等",
|
|
7
|
+
}),
|
|
8
|
+
count: Type.Optional(Type.Number({
|
|
9
|
+
description: "返回结果条数,1-10,默认 5",
|
|
10
|
+
minimum: 1,
|
|
11
|
+
maximum: 10,
|
|
12
|
+
})),
|
|
13
|
+
country: Type.Optional(Type.String({
|
|
14
|
+
description: "可选,地区代码(如 us、cn),部分 Provider 支持",
|
|
15
|
+
})),
|
|
16
|
+
freshness: Type.Optional(Type.String({
|
|
17
|
+
description: "可选,时间范围(如 pd、pw、pm),部分 Provider 支持",
|
|
18
|
+
})),
|
|
19
|
+
});
|
|
20
|
+
const WEB_SEARCH_NOOP_MESSAGE = "当前在线搜索不可用:所选 Provider 未配置或不可用(如选择了 Brave 但未配置 API Key)。请在设置中配置 Brave API Key,或将智能体在线搜索 Provider 改为 DuckDuckGo。";
|
|
21
|
+
/**
|
|
22
|
+
* 创建 web_search 工具。
|
|
23
|
+
* - enabled 为 false 时返回 null,调用方不应将工具加入 customTools。
|
|
24
|
+
* - enabled 为 true 但当前 provider 不可用(如 Brave 无 Key)时仍返回工具,执行时空转,返回固定提示。
|
|
25
|
+
*/
|
|
26
|
+
export function createWebSearchTool(options) {
|
|
27
|
+
if (!options || options.enabled !== true)
|
|
28
|
+
return null;
|
|
29
|
+
const provider = getWebSearchProvider(options.provider);
|
|
30
|
+
const apiKey = options.provider === "brave" ? options.apiKey : undefined;
|
|
31
|
+
const available = provider.isAvailable({ apiKey });
|
|
32
|
+
const timeoutSeconds = options.timeoutSeconds ?? 15;
|
|
33
|
+
const cacheTtlMinutes = options.cacheTtlMinutes ?? 5;
|
|
34
|
+
const maxResults = options.maxResults ?? 5;
|
|
35
|
+
return {
|
|
36
|
+
name: "web_search",
|
|
37
|
+
label: "Web Search",
|
|
38
|
+
description: "联网搜索:根据查询词从互联网获取最新结果(标题、链接、摘要)。无 API Key 时使用 DuckDuckGo;配置 Brave API Key 后可选用 Brave。在回答需要实时信息、新闻、天气、概念解释等问题前可调用本工具。",
|
|
39
|
+
parameters: WebSearchSchema,
|
|
40
|
+
execute: async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
|
|
41
|
+
if (!available) {
|
|
42
|
+
return {
|
|
43
|
+
content: [{ type: "text", text: WEB_SEARCH_NOOP_MESSAGE }],
|
|
44
|
+
details: undefined,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const query = (params.query ?? "").trim();
|
|
48
|
+
if (!query) {
|
|
49
|
+
return {
|
|
50
|
+
content: [{ type: "text", text: "请提供搜索关键词(query)。" }],
|
|
51
|
+
details: undefined,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const count = Math.min(10, Math.max(1, params.count ?? maxResults));
|
|
55
|
+
try {
|
|
56
|
+
const result = await provider.search({
|
|
57
|
+
query,
|
|
58
|
+
count,
|
|
59
|
+
apiKey,
|
|
60
|
+
timeoutSeconds,
|
|
61
|
+
cacheTtlMinutes,
|
|
62
|
+
country: params.country,
|
|
63
|
+
freshness: params.freshness,
|
|
64
|
+
});
|
|
65
|
+
const lines = result.results.map((r, i) => `${i + 1}. ${r.title}\n ${r.url}${r.description ? `\n ${r.description}` : ""}`);
|
|
66
|
+
let text = (result.cached ? "[缓存] " : "") +
|
|
67
|
+
`搜索「${result.query}」共 ${result.count} 条(${result.provider}${result.tookMs != null ? `, ${result.tookMs}ms` : ""}):\n\n` +
|
|
68
|
+
(lines.length ? lines.join("\n\n") : "未找到结果。");
|
|
69
|
+
const maxResultTokens = options.maxResultTokens;
|
|
70
|
+
if (typeof maxResultTokens === "number" && maxResultTokens > 0) {
|
|
71
|
+
text = truncateTextToMaxTokens(text, maxResultTokens, "web_search");
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
content: [{ type: "text", text }],
|
|
75
|
+
details: { query: result.query, count: result.count, provider: result.provider },
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
80
|
+
return {
|
|
81
|
+
content: [{ type: "text", text: `在线搜索失败: ${msg}` }],
|
|
82
|
+
details: undefined,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { createWebSearchTool } from "./create-web-search-tool.js";
|
|
2
|
+
export type { WebSearchToolOptions } from "./create-web-search-tool.js";
|
|
3
|
+
export type { WebSearchProviderId, WebSearchResultItem, WebSearchProviderResult, IWebSearchProvider, WebSearchSearchParams, } from "./types.js";
|
|
4
|
+
export { getWebSearchProvider } from "./providers/index.js";
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const PROVIDER_ID = "brave";
|
|
2
|
+
const BRAVE_WEB_SEARCH_URL = "https://api.search.brave.com/res/v1/web/search";
|
|
3
|
+
const cache = new Map();
|
|
4
|
+
function cacheKey(query, count) {
|
|
5
|
+
return `${PROVIDER_ID}:${query}:${count}`;
|
|
6
|
+
}
|
|
7
|
+
export const braveProvider = {
|
|
8
|
+
id: PROVIDER_ID,
|
|
9
|
+
isAvailable(options) {
|
|
10
|
+
return !!(options.apiKey && String(options.apiKey).trim());
|
|
11
|
+
},
|
|
12
|
+
async search(params) {
|
|
13
|
+
const query = (params.query ?? "").trim();
|
|
14
|
+
const count = Math.min(10, Math.max(1, params.count ?? 5));
|
|
15
|
+
const apiKey = params.apiKey?.trim();
|
|
16
|
+
if (!apiKey) {
|
|
17
|
+
return {
|
|
18
|
+
query,
|
|
19
|
+
provider: PROVIDER_ID,
|
|
20
|
+
results: [],
|
|
21
|
+
count: 0,
|
|
22
|
+
tookMs: 0,
|
|
23
|
+
cached: false,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const cacheTtlMinutes = typeof params.cacheTtlMinutes === "number" && params.cacheTtlMinutes >= 0 ? params.cacheTtlMinutes : 0;
|
|
27
|
+
const key = cacheKey(query, count);
|
|
28
|
+
if (cacheTtlMinutes > 0) {
|
|
29
|
+
const hit = cache.get(key);
|
|
30
|
+
if (hit && hit.expiresAt > Date.now()) {
|
|
31
|
+
return { ...hit.result, cached: true };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const timeoutMs = (params.timeoutSeconds ?? 15) * 1000;
|
|
35
|
+
const controller = new AbortController();
|
|
36
|
+
const t = setTimeout(() => controller.abort(), timeoutMs);
|
|
37
|
+
const start = Date.now();
|
|
38
|
+
try {
|
|
39
|
+
const url = new URL(BRAVE_WEB_SEARCH_URL);
|
|
40
|
+
url.searchParams.set("q", query);
|
|
41
|
+
url.searchParams.set("count", String(count));
|
|
42
|
+
if (params.country?.trim())
|
|
43
|
+
url.searchParams.set("country", params.country.trim());
|
|
44
|
+
if (params.freshness?.trim())
|
|
45
|
+
url.searchParams.set("freshness", params.freshness.trim());
|
|
46
|
+
const res = await fetch(url.toString(), {
|
|
47
|
+
method: "GET",
|
|
48
|
+
headers: {
|
|
49
|
+
Accept: "application/json",
|
|
50
|
+
"X-Subscription-Token": apiKey,
|
|
51
|
+
},
|
|
52
|
+
signal: controller.signal,
|
|
53
|
+
});
|
|
54
|
+
const tookMs = Date.now() - start;
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
const text = await res.text();
|
|
57
|
+
throw new Error(`Brave Search API error ${res.status}: ${text || res.statusText}`);
|
|
58
|
+
}
|
|
59
|
+
const data = (await res.json());
|
|
60
|
+
const rawResults = data.web?.results ?? [];
|
|
61
|
+
const results = rawResults.slice(0, count).map((r) => ({
|
|
62
|
+
title: r.title ?? "",
|
|
63
|
+
url: r.url ?? "",
|
|
64
|
+
description: r.description,
|
|
65
|
+
published: r.age,
|
|
66
|
+
}));
|
|
67
|
+
const result = {
|
|
68
|
+
query,
|
|
69
|
+
provider: PROVIDER_ID,
|
|
70
|
+
results,
|
|
71
|
+
count: results.length,
|
|
72
|
+
tookMs,
|
|
73
|
+
cached: false,
|
|
74
|
+
};
|
|
75
|
+
if (cacheTtlMinutes > 0) {
|
|
76
|
+
cache.set(key, {
|
|
77
|
+
result,
|
|
78
|
+
expiresAt: Date.now() + cacheTtlMinutes * 60 * 1000,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
clearTimeout(t);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { search as ddgSearch } from "duck-duck-scrape";
|
|
2
|
+
const PROVIDER_ID = "duck-duck-scrape";
|
|
3
|
+
const cache = new Map();
|
|
4
|
+
function cacheKey(query, count) {
|
|
5
|
+
return `${PROVIDER_ID}:${query}:${count}`;
|
|
6
|
+
}
|
|
7
|
+
export const duckDuckScrapeProvider = {
|
|
8
|
+
id: PROVIDER_ID,
|
|
9
|
+
isAvailable() {
|
|
10
|
+
return true;
|
|
11
|
+
},
|
|
12
|
+
async search(params) {
|
|
13
|
+
const query = (params.query ?? "").trim();
|
|
14
|
+
const count = Math.min(10, Math.max(1, params.count ?? 5));
|
|
15
|
+
const cacheTtlMinutes = typeof params.cacheTtlMinutes === "number" && params.cacheTtlMinutes >= 0 ? params.cacheTtlMinutes : 0;
|
|
16
|
+
const key = cacheKey(query, count);
|
|
17
|
+
if (cacheTtlMinutes > 0) {
|
|
18
|
+
const hit = cache.get(key);
|
|
19
|
+
if (hit && hit.expiresAt > Date.now()) {
|
|
20
|
+
return { ...hit.result, cached: true };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const start = Date.now();
|
|
24
|
+
const res = await ddgSearch(query, {});
|
|
25
|
+
const tookMs = Date.now() - start;
|
|
26
|
+
const results = (res.results ?? []).slice(0, count).map((r) => ({
|
|
27
|
+
title: r.title ?? "",
|
|
28
|
+
url: r.url ?? "",
|
|
29
|
+
description: r.description,
|
|
30
|
+
}));
|
|
31
|
+
const result = {
|
|
32
|
+
query,
|
|
33
|
+
provider: PROVIDER_ID,
|
|
34
|
+
results,
|
|
35
|
+
count: results.length,
|
|
36
|
+
tookMs,
|
|
37
|
+
cached: false,
|
|
38
|
+
};
|
|
39
|
+
if (cacheTtlMinutes > 0) {
|
|
40
|
+
cache.set(key, {
|
|
41
|
+
result,
|
|
42
|
+
expiresAt: Date.now() + cacheTtlMinutes * 60 * 1000,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
},
|
|
47
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { WebSearchProviderId, IWebSearchProvider } from "../types.js";
|
|
2
|
+
import { duckDuckScrapeProvider } from "./duck-duck-scrape.js";
|
|
3
|
+
import { braveProvider } from "./brave.js";
|
|
4
|
+
export declare function getWebSearchProvider(id: WebSearchProviderId): IWebSearchProvider;
|
|
5
|
+
export { duckDuckScrapeProvider, braveProvider };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { duckDuckScrapeProvider } from "./duck-duck-scrape.js";
|
|
2
|
+
import { braveProvider } from "./brave.js";
|
|
3
|
+
const providers = {
|
|
4
|
+
"duck-duck-scrape": duckDuckScrapeProvider,
|
|
5
|
+
brave: braveProvider,
|
|
6
|
+
};
|
|
7
|
+
export function getWebSearchProvider(id) {
|
|
8
|
+
const p = providers[id];
|
|
9
|
+
if (!p)
|
|
10
|
+
throw new Error(`Unknown web search provider: ${id}`);
|
|
11
|
+
return p;
|
|
12
|
+
}
|
|
13
|
+
export { duckDuckScrapeProvider, braveProvider };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 在线搜索 Provider 统一类型,供 web_search 工具与多 Provider 实现使用。
|
|
3
|
+
*/
|
|
4
|
+
export type WebSearchProviderId = "duck-duck-scrape" | "brave";
|
|
5
|
+
export interface WebSearchResultItem {
|
|
6
|
+
title: string;
|
|
7
|
+
url: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
published?: string;
|
|
10
|
+
siteName?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface WebSearchProviderResult {
|
|
13
|
+
query: string;
|
|
14
|
+
provider: WebSearchProviderId;
|
|
15
|
+
results: WebSearchResultItem[];
|
|
16
|
+
count: number;
|
|
17
|
+
tookMs?: number;
|
|
18
|
+
cached?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface WebSearchSearchParams {
|
|
21
|
+
query: string;
|
|
22
|
+
count?: number;
|
|
23
|
+
apiKey?: string;
|
|
24
|
+
timeoutSeconds?: number;
|
|
25
|
+
cacheTtlMinutes?: number;
|
|
26
|
+
country?: string;
|
|
27
|
+
freshness?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface IWebSearchProvider {
|
|
30
|
+
id: WebSearchProviderId;
|
|
31
|
+
isAvailable(options: {
|
|
32
|
+
apiKey?: string;
|
|
33
|
+
}): boolean;
|
|
34
|
+
search(params: WebSearchSearchParams): Promise<WebSearchProviderResult>;
|
|
35
|
+
}
|
|
@@ -145,7 +145,7 @@ async function handleAgentChatInner(client, targetSessionId, message, params) {
|
|
|
145
145
|
apiKey = agentConfig.apiKey;
|
|
146
146
|
}
|
|
147
147
|
const runnerType = agentConfig?.runnerType ?? "local";
|
|
148
|
-
const isProxyAgent = runnerType === "coze" || runnerType === "openclawx" || runnerType === "opencode";
|
|
148
|
+
const isProxyAgent = runnerType === "coze" || runnerType === "openclawx" || runnerType === "opencode" || runnerType === "claude_code";
|
|
149
149
|
if (isProxyAgent) {
|
|
150
150
|
console.log(`[agent.chat] Using proxy agent (${runnerType}) for session=${targetSessionId}, agentId=${currentAgentId}`);
|
|
151
151
|
}
|
|
@@ -211,8 +211,10 @@ async function handleAgentChatInner(client, targetSessionId, message, params) {
|
|
|
211
211
|
maxSessions: maxAgentSessions,
|
|
212
212
|
targetAgentId: effectiveTargetAgentId,
|
|
213
213
|
mcpServers: agentConfig?.mcpServers,
|
|
214
|
+
mcpMaxResultTokens: agentConfig?.mcpMaxResultTokens,
|
|
214
215
|
systemPrompt: agentConfig?.systemPrompt,
|
|
215
216
|
useLongMemory: agentConfig?.useLongMemory,
|
|
217
|
+
webSearch: agentConfig?.webSearch,
|
|
216
218
|
});
|
|
217
219
|
}
|
|
218
220
|
catch (err) {
|
|
@@ -61,8 +61,10 @@ export async function handleRunScheduledTask(req, res) {
|
|
|
61
61
|
modelId,
|
|
62
62
|
apiKey,
|
|
63
63
|
mcpServers: agentConfig?.mcpServers,
|
|
64
|
+
mcpMaxResultTokens: agentConfig?.mcpMaxResultTokens,
|
|
64
65
|
systemPrompt: agentConfig?.systemPrompt,
|
|
65
66
|
useLongMemory: agentConfig?.useLongMemory,
|
|
67
|
+
webSearch: agentConfig?.webSearch,
|
|
66
68
|
});
|
|
67
69
|
let assistantContent = "";
|
|
68
70
|
let turnPromptTokens = 0;
|
|
@@ -25,7 +25,7 @@ export declare class AgentConfigController {
|
|
|
25
25
|
success: boolean;
|
|
26
26
|
data: AgentConfigItem;
|
|
27
27
|
}>;
|
|
28
|
-
updateAgent(id: string, body: Partial<Pick<AgentConfigItem, 'name' | 'provider' | 'model' | 'modelItemCode' | 'mcpServers' | 'systemPrompt' | 'icon' | 'runnerType' | 'coze' | 'openclawx' | 'opencode'>>): Promise<{
|
|
28
|
+
updateAgent(id: string, body: Partial<Pick<AgentConfigItem, 'name' | 'provider' | 'model' | 'modelItemCode' | 'mcpServers' | 'mcpMaxResultTokens' | 'systemPrompt' | 'icon' | 'runnerType' | 'coze' | 'openclawx' | 'opencode' | 'claudeCode' | 'useLongMemory' | 'webSearch'>>): Promise<{
|
|
29
29
|
success: boolean;
|
|
30
30
|
data: AgentConfigItem;
|
|
31
31
|
}>;
|
|
@@ -3,8 +3,8 @@ import { DatabaseService } from '../database/database.service.js';
|
|
|
3
3
|
import { WorkspaceService } from '../workspace/workspace.service.js';
|
|
4
4
|
/** 缺省智能体 ID / 工作空间名,不可删除;对应目录 ~/.openbot/workspace/default */
|
|
5
5
|
export declare const DEFAULT_AGENT_ID = "default";
|
|
6
|
-
/** 执行器类型:local=本机,coze/openclawx/opencode
|
|
7
|
-
export type AgentRunnerType = 'local' | 'coze' | 'openclawx' | 'opencode';
|
|
6
|
+
/** 执行器类型:local=本机,coze/openclawx/opencode=远程代理,claude_code=本机 Claude Code CLI */
|
|
7
|
+
export type AgentRunnerType = 'local' | 'coze' | 'openclawx' | 'opencode' | 'claude_code';
|
|
8
8
|
/** Coze 站点:cn=国内 api.coze.cn,com=国际 api.coze.com,凭证不通用 */
|
|
9
9
|
export type CozeRegion = 'cn' | 'com';
|
|
10
10
|
/** 某站点的 Bot 凭证 */
|
|
@@ -60,6 +60,8 @@ export interface AgentConfigItem {
|
|
|
60
60
|
isDefault?: boolean;
|
|
61
61
|
/** MCP 配置:数组(含 transport)或标准 JSON 对象(key 为服务器名称),创建 Session 时归一化使用 */
|
|
62
62
|
mcpServers?: McpServerConfig[] | McpServersStandardFormat;
|
|
63
|
+
/** MCP 单次返回最大 token;不配置则不限制 */
|
|
64
|
+
mcpMaxResultTokens?: number;
|
|
63
65
|
/** 自定义系统提示词,会与技能等一起组成最终 systemPrompt */
|
|
64
66
|
systemPrompt?: string;
|
|
65
67
|
/** 智能体图标标识(前端预设图标 id,如 default、star、code 等) */
|
|
@@ -72,8 +74,18 @@ export interface AgentConfigItem {
|
|
|
72
74
|
openclawx?: AgentOpenClawXConfig;
|
|
73
75
|
/** OpenCode 代理配置(runnerType 为 opencode 时使用) */
|
|
74
76
|
opencode?: AgentOpenCodeConfig;
|
|
77
|
+
/** Claude Code 代理配置(runnerType 为 claude_code 时使用):工作目录等 */
|
|
78
|
+
claudeCode?: {
|
|
79
|
+
workingDirectory?: string;
|
|
80
|
+
};
|
|
75
81
|
/** 是否使用经验(长记忆):memory_recall / save_experience;默认 true */
|
|
76
82
|
useLongMemory?: boolean;
|
|
83
|
+
/** 在线搜索:启用后该智能体拥有 web_search 工具;可选默认 provider、maxResultTokens(前端默认 64K) */
|
|
84
|
+
webSearch?: {
|
|
85
|
+
enabled?: boolean;
|
|
86
|
+
provider?: 'brave' | 'duck-duck-scrape';
|
|
87
|
+
maxResultTokens?: number;
|
|
88
|
+
};
|
|
77
89
|
}
|
|
78
90
|
export interface DeleteAgentOptions {
|
|
79
91
|
/** 是否同时删除该工作区在磁盘上的目录及文件;默认 false(仅删数据库中的工作区相关数据,保留目录) */
|
|
@@ -104,7 +116,7 @@ export declare class AgentConfigService {
|
|
|
104
116
|
/** 智能体图标标识 */
|
|
105
117
|
icon?: string;
|
|
106
118
|
}): Promise<AgentConfigItem>;
|
|
107
|
-
updateAgent(id: string, updates: Partial<Pick<AgentConfigItem, 'name' | 'provider' | 'model' | 'modelItemCode' | 'mcpServers' | 'systemPrompt' | 'icon' | 'runnerType' | 'coze' | 'openclawx' | 'opencode' | 'useLongMemory'>>): Promise<AgentConfigItem>;
|
|
119
|
+
updateAgent(id: string, updates: Partial<Pick<AgentConfigItem, 'name' | 'provider' | 'model' | 'modelItemCode' | 'mcpServers' | 'mcpMaxResultTokens' | 'systemPrompt' | 'icon' | 'runnerType' | 'coze' | 'openclawx' | 'opencode' | 'claudeCode' | 'useLongMemory' | 'webSearch'>>): Promise<AgentConfigItem>;
|
|
108
120
|
deleteAgent(id: string, options?: DeleteAgentOptions): Promise<void>;
|
|
109
121
|
/** 仅删除数据库中与该工作区相关的数据(会话、定时任务、收藏等),不删磁盘目录 */
|
|
110
122
|
private deleteWorkspaceDataFromDb;
|
|
@@ -162,6 +162,8 @@ let AgentConfigService = class AgentConfigService {
|
|
|
162
162
|
agent.modelItemCode = updates.modelItemCode;
|
|
163
163
|
if (updates.mcpServers !== undefined)
|
|
164
164
|
agent.mcpServers = updates.mcpServers;
|
|
165
|
+
if (updates.mcpMaxResultTokens !== undefined)
|
|
166
|
+
agent.mcpMaxResultTokens = updates.mcpMaxResultTokens;
|
|
165
167
|
if (updates.systemPrompt !== undefined)
|
|
166
168
|
agent.systemPrompt = updates.systemPrompt?.trim() || undefined;
|
|
167
169
|
if (updates.icon !== undefined)
|
|
@@ -211,8 +213,24 @@ let AgentConfigService = class AgentConfigService {
|
|
|
211
213
|
agent.openclawx = updates.openclawx;
|
|
212
214
|
if (updates.opencode !== undefined)
|
|
213
215
|
agent.opencode = updates.opencode;
|
|
216
|
+
if (updates.claudeCode !== undefined)
|
|
217
|
+
agent.claudeCode = updates.claudeCode;
|
|
214
218
|
if (updates.useLongMemory !== undefined)
|
|
215
219
|
agent.useLongMemory = updates.useLongMemory;
|
|
220
|
+
if (updates.webSearch !== undefined) {
|
|
221
|
+
agent.webSearch =
|
|
222
|
+
updates.webSearch && (updates.webSearch.enabled || updates.webSearch.provider)
|
|
223
|
+
? {
|
|
224
|
+
enabled: !!updates.webSearch.enabled,
|
|
225
|
+
provider: updates.webSearch.provider === 'brave' || updates.webSearch.provider === 'duck-duck-scrape'
|
|
226
|
+
? updates.webSearch.provider
|
|
227
|
+
: 'duck-duck-scrape',
|
|
228
|
+
maxResultTokens: updates.webSearch.maxResultTokens != null && typeof updates.webSearch.maxResultTokens === 'number' && updates.webSearch.maxResultTokens > 0
|
|
229
|
+
? updates.webSearch.maxResultTokens
|
|
230
|
+
: undefined,
|
|
231
|
+
}
|
|
232
|
+
: undefined;
|
|
233
|
+
}
|
|
216
234
|
await this.writeAgentsFile(file);
|
|
217
235
|
await addPendingAgentReload(id).catch(() => { });
|
|
218
236
|
return { ...agent, isDefault: agent.id === DEFAULT_AGENT_ID };
|
|
@@ -39,6 +39,19 @@ export declare class ConfigController {
|
|
|
39
39
|
embeddingModelItemCode?: string;
|
|
40
40
|
};
|
|
41
41
|
channels?: import("../../core/config/desktop-config.js").ChannelsConfig;
|
|
42
|
+
tools?: {
|
|
43
|
+
webSearch?: {
|
|
44
|
+
defaultProvider?: "brave" | "duck-duck-scrape";
|
|
45
|
+
providers?: {
|
|
46
|
+
brave?: {
|
|
47
|
+
apiKey?: string;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
timeoutSeconds?: number;
|
|
51
|
+
cacheTtlMinutes?: number;
|
|
52
|
+
maxResults?: number;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
42
55
|
};
|
|
43
56
|
}>;
|
|
44
57
|
updateConfig(updates: Partial<AppConfig>): Promise<{
|
|
@@ -78,6 +91,19 @@ export declare class ConfigController {
|
|
|
78
91
|
embeddingModelItemCode?: string;
|
|
79
92
|
};
|
|
80
93
|
channels?: import("../../core/config/desktop-config.js").ChannelsConfig;
|
|
94
|
+
tools?: {
|
|
95
|
+
webSearch?: {
|
|
96
|
+
defaultProvider?: "brave" | "duck-duck-scrape";
|
|
97
|
+
providers?: {
|
|
98
|
+
brave?: {
|
|
99
|
+
apiKey?: string;
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
timeoutSeconds?: number;
|
|
103
|
+
cacheTtlMinutes?: number;
|
|
104
|
+
maxResults?: number;
|
|
105
|
+
};
|
|
106
|
+
};
|
|
81
107
|
};
|
|
82
108
|
}>;
|
|
83
109
|
getProviders(): Promise<{
|
|
@@ -71,6 +71,20 @@ export interface AppConfig {
|
|
|
71
71
|
};
|
|
72
72
|
/** 通道配置:飞书、Telegram 等 token/key */
|
|
73
73
|
channels?: ChannelsConfig;
|
|
74
|
+
/** 工具相关:在线搜索等,Brave API Key 存于 providers.brave.apiKey */
|
|
75
|
+
tools?: {
|
|
76
|
+
webSearch?: {
|
|
77
|
+
defaultProvider?: 'brave' | 'duck-duck-scrape';
|
|
78
|
+
providers?: {
|
|
79
|
+
brave?: {
|
|
80
|
+
apiKey?: string;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
timeoutSeconds?: number;
|
|
84
|
+
cacheTtlMinutes?: number;
|
|
85
|
+
maxResults?: number;
|
|
86
|
+
};
|
|
87
|
+
};
|
|
74
88
|
}
|
|
75
89
|
export declare class ConfigService {
|
|
76
90
|
private readonly agentConfigService;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.8.
|
|
6
|
+
"version": "0.8.40",
|
|
7
7
|
"description": "OpenClawX - A professional desktop application for managing and executing AI agents with real-time chat, session management, and skills browsing.",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"main": "dist/index.js",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"openbot": "./dist/cli/cli.js"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
+
"@instantlyeasy/claude-code-sdk-ts": "^0.3.3",
|
|
29
30
|
"@larksuiteoapi/node-sdk": "^1.59.0",
|
|
30
31
|
"@mariozechner/pi-ai": "^0.51.2",
|
|
31
32
|
"@mariozechner/pi-coding-agent": "^0.51.2",
|
|
@@ -41,6 +42,7 @@
|
|
|
41
42
|
"commander": "^14.0.2",
|
|
42
43
|
"croner": "^9.1.0",
|
|
43
44
|
"dingtalk-stream": "^2.1.4",
|
|
45
|
+
"duck-duck-scrape": "^2.2.7",
|
|
44
46
|
"multer": "^1.4.5-lts.1",
|
|
45
47
|
"node-llama-cpp": "^3.15.0",
|
|
46
48
|
"qrcode": "^1.5.4",
|