@next-open-ai/openclawx 0.8.32 → 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 (78) hide show
  1. package/README.md +55 -47
  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 +72 -6
  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/client.d.ts +4 -0
  29. package/dist/core/mcp/client.js +2 -0
  30. package/dist/core/mcp/index.d.ts +5 -1
  31. package/dist/core/mcp/index.js +5 -1
  32. package/dist/core/mcp/operator.d.ts +19 -2
  33. package/dist/core/mcp/operator.js +97 -30
  34. package/dist/core/mcp/transport/index.d.ts +4 -0
  35. package/dist/core/mcp/transport/index.js +6 -1
  36. package/dist/core/mcp/transport/stdio.d.ts +6 -0
  37. package/dist/core/mcp/transport/stdio.js +22 -1
  38. package/dist/core/session-outlet/index.d.ts +19 -0
  39. package/dist/core/session-outlet/index.js +33 -0
  40. package/dist/core/session-outlet/outlet.d.ts +15 -0
  41. package/dist/core/session-outlet/outlet.js +49 -0
  42. package/dist/core/session-outlet/types.d.ts +35 -0
  43. package/dist/core/session-outlet/types.js +5 -0
  44. package/dist/core/tools/index.d.ts +1 -0
  45. package/dist/core/tools/index.js +1 -0
  46. package/dist/core/tools/truncate-result.d.ts +14 -0
  47. package/dist/core/tools/truncate-result.js +27 -0
  48. package/dist/core/tools/web-search/create-web-search-tool.d.ts +17 -0
  49. package/dist/core/tools/web-search/create-web-search-tool.js +87 -0
  50. package/dist/core/tools/web-search/index.d.ts +4 -0
  51. package/dist/core/tools/web-search/index.js +2 -0
  52. package/dist/core/tools/web-search/providers/brave.d.ts +2 -0
  53. package/dist/core/tools/web-search/providers/brave.js +87 -0
  54. package/dist/core/tools/web-search/providers/duck-duck-scrape.d.ts +2 -0
  55. package/dist/core/tools/web-search/providers/duck-duck-scrape.js +47 -0
  56. package/dist/core/tools/web-search/providers/index.d.ts +5 -0
  57. package/dist/core/tools/web-search/providers/index.js +13 -0
  58. package/dist/core/tools/web-search/types.d.ts +35 -0
  59. package/dist/core/tools/web-search/types.js +4 -0
  60. package/dist/gateway/channel/channel-core.d.ts +1 -0
  61. package/dist/gateway/channel/channel-core.js +91 -70
  62. package/dist/gateway/methods/agent-cancel.js +3 -0
  63. package/dist/gateway/methods/agent-chat.d.ts +4 -0
  64. package/dist/gateway/methods/agent-chat.js +294 -240
  65. package/dist/gateway/methods/run-scheduled-task.js +2 -0
  66. package/dist/gateway/server.js +2 -0
  67. package/dist/server/agent-config/agent-config.controller.d.ts +1 -1
  68. package/dist/server/agent-config/agent-config.service.d.ts +15 -3
  69. package/dist/server/agent-config/agent-config.service.js +18 -0
  70. package/dist/server/config/config.controller.d.ts +26 -0
  71. package/dist/server/config/config.service.d.ts +14 -0
  72. package/package.json +3 -1
  73. package/presets/preset-agents.json +121 -91
  74. package/presets/workspaces/finance-expert/skills/akshare-helper/SKILL.md +9 -0
  75. package/presets/workspaces/office-automation/skills/rpa-helper/SKILL.md +9 -0
  76. package/presets/workspaces/self-media-bot/skills/self-media-tools/SKILL.md +9 -0
  77. package/apps/desktop/renderer/dist/assets/index-BGHtXhm3.js +0 -89
  78. package/apps/desktop/renderer/dist/assets/index-CB2-m4ae.css +0 -10
@@ -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 {};
@@ -1,5 +1,6 @@
1
1
  /**
2
2
  * 通道核心:收 UnifiedMessage → 会话映射 → 调 Agent → 选 Outbound → 发 UnifiedReply。
3
+ * 系统消息(// 命令结果、MCP 进度)经统一出口推送,通道注册消费者后按 outbound 发送。
3
4
  */
4
5
  import type { UnifiedMessage, IChannel } from "./types.js";
5
6
  /**
@@ -2,6 +2,10 @@ import { runAgentAndCollectReply, runAgentAndStreamReply } from "./run-agent.js"
2
2
  import { getChannelSessionPersistence, persistChannelUserMessage, persistChannelAssistantMessage } from "./session-persistence.js";
3
3
  import { preprocessInboundMessage } from "../../core/inbound-message-preprocess.js";
4
4
  import { getSessionCurrentAgentUpdater } from "../../core/session-current-agent.js";
5
+ import { getSessionOutlet, sendSessionMessage } from "../../core/session-outlet/index.js";
6
+ /** 系统消息前缀,与正常回复同路下发,仅用此前缀区分,各端无需额外逻辑;结尾换行便于与正文分行 */
7
+ const SYSTEM_MSG_PREFIX = "[System Message] ";
8
+ const SYSTEM_MSG_SUFFIX = "\n";
5
9
  const STREAM_THROTTLE_MS = 280;
6
10
  function toSessionId(channelId, threadId) {
7
11
  return `channel:${channelId}:${threadId}`;
@@ -70,87 +74,104 @@ export async function handleChannelMessage(channel, msg) {
70
74
  currentAgentId,
71
75
  });
72
76
  getSessionCurrentAgentUpdater()?.(sessionId, effectiveAgentId);
73
- if (directResponse != null && directResponse !== "") {
77
+ const outlet = getSessionOutlet();
78
+ const channelConsumer = {
79
+ send(m) {
80
+ if (m.type !== "system")
81
+ return;
82
+ const raw = m.payload?.text ?? m.payload?.message ?? m.payload?.phase ?? "";
83
+ if (raw) {
84
+ const text = SYSTEM_MSG_PREFIX + raw + SYSTEM_MSG_SUFFIX;
85
+ outbound.send(threadId, { text }).catch((e) => console.warn("[ChannelCore] outlet send failed:", e));
86
+ }
87
+ },
88
+ };
89
+ const unregisterConsumer = outlet ? outlet.registerConsumer(sessionId, channelConsumer) : () => { };
90
+ try {
91
+ if (directResponse != null && directResponse !== "") {
92
+ await persistChannelUserMessage(sessionId, {
93
+ agentId: effectiveAgentId,
94
+ title: rawMessage.slice(0, 50) || undefined,
95
+ messageText: rawMessage,
96
+ });
97
+ persistChannelAssistantMessage(sessionId, directResponse);
98
+ sendSessionMessage(sessionId, { type: "system", code: "command.result", payload: { text: directResponse } });
99
+ await msg.ack?.({ text: directResponse });
100
+ return;
101
+ }
102
+ // 与 Web/Desktop 统一:通道会话入库并追加用户消息(用预处理后的 agentId 与消息)
74
103
  await persistChannelUserMessage(sessionId, {
75
104
  agentId: effectiveAgentId,
76
- title: rawMessage.slice(0, 50) || undefined,
77
- messageText: rawMessage,
105
+ title: messageText.slice(0, 50) || undefined,
106
+ messageText,
78
107
  });
79
- persistChannelAssistantMessage(sessionId, directResponse);
80
- const reply = { text: directResponse };
81
- await outbound.send(threadId, reply);
82
- await msg.ack?.(reply);
83
- return;
84
- }
85
- // Web/Desktop 统一:通道会话入库并追加用户消息(用预处理后的 agentId 与消息)
86
- await persistChannelUserMessage(sessionId, {
87
- agentId: effectiveAgentId,
88
- title: messageText.slice(0, 50) || undefined,
89
- messageText,
90
- });
91
- const useStream = typeof outbound.sendStream === "function";
92
- if (useStream) {
93
- let sink;
94
- try {
95
- sink = await outbound.sendStream(threadId);
96
- }
97
- catch (err) {
98
- console.error("[ChannelCore] sendStream init failed:", err);
99
- const reply = { text: "发信失败,请稍后再试。" };
100
- await outbound.send(threadId, reply);
108
+ const useStream = typeof outbound.sendStream === "function";
109
+ if (useStream) {
110
+ let sink;
111
+ try {
112
+ sink = await outbound.sendStream(threadId);
113
+ }
114
+ catch (err) {
115
+ console.error("[ChannelCore] sendStream init failed:", err);
116
+ const reply = { text: "发信失败,请稍后再试。" };
117
+ await outbound.send(threadId, reply);
118
+ return;
119
+ }
120
+ let accumulated = "";
121
+ let donePromise = null;
122
+ const throttled = throttle(() => {
123
+ if (sink.onChunk)
124
+ void Promise.resolve(sink.onChunk(accumulated)).catch((e) => console.error("[ChannelCore] stream onChunk error:", e));
125
+ }, STREAM_THROTTLE_MS);
126
+ try {
127
+ await runAgentAndStreamReply({ sessionId, message: messageText, agentId: effectiveAgentId }, {
128
+ onChunk(delta) {
129
+ accumulated += delta;
130
+ throttled.run();
131
+ },
132
+ onTurnEnd() {
133
+ throttled.flush();
134
+ if (sink.onTurnEnd)
135
+ void Promise.resolve(sink.onTurnEnd(accumulated)).catch((e) => console.error("[ChannelCore] stream onTurnEnd error:", e));
136
+ },
137
+ onDone() {
138
+ throttled.cancel();
139
+ const final = accumulated.trim() || "(无文本回复)";
140
+ persistChannelAssistantMessage(sessionId, final);
141
+ donePromise = Promise.resolve(sink.onDone(final)).catch((e) => console.error("[ChannelCore] stream onDone error:", e));
142
+ },
143
+ });
144
+ if (donePromise)
145
+ await donePromise;
146
+ await msg.ack?.(undefined);
147
+ }
148
+ catch (err) {
149
+ console.error("[ChannelCore] runAgent failed:", err);
150
+ throttled.cancel();
151
+ const fallback = accumulated.trim() || "处理时出错,请稍后再试。";
152
+ await Promise.resolve(sink.onDone(fallback)).catch(() => { });
153
+ await msg.ack?.(undefined);
154
+ }
101
155
  return;
102
156
  }
103
- let accumulated = "";
104
- let donePromise = null;
105
- const throttled = throttle(() => {
106
- if (sink.onChunk)
107
- void Promise.resolve(sink.onChunk(accumulated)).catch((e) => console.error("[ChannelCore] stream onChunk error:", e));
108
- }, STREAM_THROTTLE_MS);
157
+ let replyText;
109
158
  try {
110
- await runAgentAndStreamReply({ sessionId, message: messageText, agentId: effectiveAgentId }, {
111
- onChunk(delta) {
112
- accumulated += delta;
113
- throttled.run();
114
- },
115
- onTurnEnd() {
116
- throttled.flush();
117
- if (sink.onTurnEnd)
118
- void Promise.resolve(sink.onTurnEnd(accumulated)).catch((e) => console.error("[ChannelCore] stream onTurnEnd error:", e));
119
- },
120
- onDone() {
121
- throttled.cancel();
122
- const final = accumulated.trim() || "(无文本回复)";
123
- persistChannelAssistantMessage(sessionId, final);
124
- donePromise = Promise.resolve(sink.onDone(final)).catch((e) => console.error("[ChannelCore] stream onDone error:", e));
125
- },
159
+ replyText = await runAgentAndCollectReply({
160
+ sessionId,
161
+ message: messageText,
162
+ agentId: effectiveAgentId,
126
163
  });
127
- if (donePromise)
128
- await donePromise;
129
- await msg.ack?.(undefined);
130
164
  }
131
165
  catch (err) {
132
166
  console.error("[ChannelCore] runAgent failed:", err);
133
- throttled.cancel();
134
- const fallback = accumulated.trim() || "处理时出错,请稍后再试。";
135
- await Promise.resolve(sink.onDone(fallback)).catch(() => { });
136
- await msg.ack?.(undefined);
167
+ replyText = "处理时出错,请稍后再试。";
137
168
  }
138
- return;
139
- }
140
- let replyText;
141
- try {
142
- replyText = await runAgentAndCollectReply({
143
- sessionId,
144
- message: messageText,
145
- agentId: effectiveAgentId,
146
- });
169
+ persistChannelAssistantMessage(sessionId, replyText);
170
+ const reply = { text: replyText };
171
+ const sendResult = await outbound.send(threadId, reply);
172
+ await msg.ack?.(sendResult);
147
173
  }
148
- catch (err) {
149
- console.error("[ChannelCore] runAgent failed:", err);
150
- replyText = "处理时出错,请稍后再试。";
174
+ finally {
175
+ unregisterConsumer();
151
176
  }
152
- persistChannelAssistantMessage(sessionId, replyText);
153
- const reply = { text: replyText };
154
- const sendResult = await outbound.send(threadId, reply);
155
- await msg.ack?.(sendResult);
156
177
  }
@@ -1,5 +1,6 @@
1
1
  import { agentManager } from "../../core/agent/agent-manager.js";
2
2
  import { abortProxyRun } from "../proxy-run-abort.js";
3
+ import { clearSessionStreamSubscription } from "./agent-chat.js";
3
4
  const COMPOSITE_KEY_SEP = "::";
4
5
  /**
5
6
  * Handle agent.cancel: abort the current turn for the given session.
@@ -13,8 +14,10 @@ export async function handleAgentCancel(client, params) {
13
14
  }
14
15
  const agentId = params?.agentId ?? client.agentId ?? "default";
15
16
  if (abortProxyRun(sessionId, agentId)) {
17
+ clearSessionStreamSubscription(sessionId);
16
18
  return { status: "aborted" };
17
19
  }
20
+ clearSessionStreamSubscription(sessionId);
18
21
  const compositeKey = sessionId + COMPOSITE_KEY_SEP + agentId;
19
22
  let session = agentManager.getSession(compositeKey);
20
23
  if (!session) {
@@ -1,4 +1,8 @@
1
1
  import type { GatewayClient, AgentChatParams } from "../types.js";
2
+ /**
3
+ * 移除某 session 的流式订阅(cancel 或新消息开始时调用,避免同一事件被多个回调处理导致界面重复)
4
+ */
5
+ export declare function clearSessionStreamSubscription(sessionId: string): void;
2
6
  /**
3
7
  * Handle agent chat request with streaming support
4
8
  */