@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.
Files changed (61) hide show
  1. package/README.md +50 -42
  2. package/apps/desktop/renderer/dist/assets/index-BSfTiTKo.css +10 -0
  3. package/apps/desktop/renderer/dist/assets/index-DgLpQsA-.js +89 -0
  4. package/apps/desktop/renderer/dist/index.html +2 -2
  5. package/dist/cli/cli.js +29 -0
  6. package/dist/cli/extension-cmd.d.ts +15 -0
  7. package/dist/cli/extension-cmd.js +107 -0
  8. package/dist/core/agent/agent-dir.d.ts +6 -0
  9. package/dist/core/agent/agent-dir.js +8 -0
  10. package/dist/core/agent/agent-manager.d.ts +13 -0
  11. package/dist/core/agent/agent-manager.js +68 -5
  12. package/dist/core/agent/proxy/adapters/claude-code-adapter.d.ts +2 -0
  13. package/dist/core/agent/proxy/adapters/claude-code-adapter.js +186 -0
  14. package/dist/core/agent/proxy/adapters/local-adapter.js +2 -0
  15. package/dist/core/agent/proxy/adapters/opencode-adapter.js +65 -29
  16. package/dist/core/agent/proxy/adapters/opencode-local-runner.js +9 -0
  17. package/dist/core/agent/proxy/index.js +2 -0
  18. package/dist/core/agent/token-usage-log-extension.d.ts +14 -0
  19. package/dist/core/agent/token-usage-log-extension.js +61 -0
  20. package/dist/core/config/desktop-config.d.ts +22 -2
  21. package/dist/core/config/desktop-config.js +57 -1
  22. package/dist/core/extensions/index.d.ts +1 -0
  23. package/dist/core/extensions/index.js +1 -0
  24. package/dist/core/extensions/load.d.ts +11 -0
  25. package/dist/core/extensions/load.js +101 -0
  26. package/dist/core/mcp/adapter.d.ts +4 -2
  27. package/dist/core/mcp/adapter.js +10 -4
  28. package/dist/core/mcp/index.d.ts +2 -0
  29. package/dist/core/mcp/index.js +1 -0
  30. package/dist/core/mcp/operator.d.ts +2 -0
  31. package/dist/core/mcp/operator.js +1 -1
  32. package/dist/core/tools/index.d.ts +1 -0
  33. package/dist/core/tools/index.js +1 -0
  34. package/dist/core/tools/truncate-result.d.ts +14 -0
  35. package/dist/core/tools/truncate-result.js +27 -0
  36. package/dist/core/tools/web-search/create-web-search-tool.d.ts +17 -0
  37. package/dist/core/tools/web-search/create-web-search-tool.js +87 -0
  38. package/dist/core/tools/web-search/index.d.ts +4 -0
  39. package/dist/core/tools/web-search/index.js +2 -0
  40. package/dist/core/tools/web-search/providers/brave.d.ts +2 -0
  41. package/dist/core/tools/web-search/providers/brave.js +87 -0
  42. package/dist/core/tools/web-search/providers/duck-duck-scrape.d.ts +2 -0
  43. package/dist/core/tools/web-search/providers/duck-duck-scrape.js +47 -0
  44. package/dist/core/tools/web-search/providers/index.d.ts +5 -0
  45. package/dist/core/tools/web-search/providers/index.js +13 -0
  46. package/dist/core/tools/web-search/types.d.ts +35 -0
  47. package/dist/core/tools/web-search/types.js +4 -0
  48. package/dist/gateway/methods/agent-chat.js +3 -1
  49. package/dist/gateway/methods/run-scheduled-task.js +2 -0
  50. package/dist/server/agent-config/agent-config.controller.d.ts +1 -1
  51. package/dist/server/agent-config/agent-config.service.d.ts +15 -3
  52. package/dist/server/agent-config/agent-config.service.js +18 -0
  53. package/dist/server/config/config.controller.d.ts +26 -0
  54. package/dist/server/config/config.service.d.ts +14 -0
  55. package/package.json +3 -1
  56. package/presets/preset-agents.json +121 -91
  57. package/presets/workspaces/finance-expert/skills/akshare-helper/SKILL.md +9 -0
  58. package/presets/workspaces/office-automation/skills/rpa-helper/SKILL.md +9 -0
  59. package/presets/workspaces/self-media-bot/skills/self-media-tools/SKILL.md +9 -0
  60. package/apps/desktop/renderer/dist/assets/index-BGHtXhm3.js +0 -89
  61. 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";
@@ -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,2 @@
1
+ export { createWebSearchTool } from "./create-web-search-tool.js";
2
+ export { getWebSearchProvider } from "./providers/index.js";
@@ -0,0 +1,2 @@
1
+ import type { IWebSearchProvider } from "../types.js";
2
+ export declare const braveProvider: IWebSearchProvider;
@@ -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,2 @@
1
+ import type { IWebSearchProvider } from "../types.js";
2
+ export declare const duckDuckScrapeProvider: IWebSearchProvider;
@@ -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
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * 在线搜索 Provider 统一类型,供 web_search 工具与多 Provider 实现使用。
3
+ */
4
+ export {};
@@ -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.36",
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",