@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
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
import { agentManager } from "../../core/agent/agent-manager.js";
|
|
2
|
+
import { runForChannelStream } from "../../core/agent/proxy/index.js";
|
|
2
3
|
import { getSessionCurrentAgentResolver, getSessionCurrentAgentUpdater } from "../../core/session-current-agent.js";
|
|
3
|
-
import {
|
|
4
|
+
import { preprocessInboundMessage } from "../../core/inbound-message-preprocess.js";
|
|
4
5
|
import { send, createEvent } from "../utils.js";
|
|
5
6
|
import { connectedClients } from "../clients.js";
|
|
6
7
|
import { getDesktopConfig, loadDesktopAgentConfig } from "../../core/config/desktop-config.js";
|
|
8
|
+
import { consumePendingAgentReload } from "../../core/config/agent-reload-pending.js";
|
|
9
|
+
import { registerProxyRunAbort } from "../proxy-run-abort.js";
|
|
10
|
+
import { getSessionOutlet, sendSessionMessage } from "../../core/session-outlet/index.js";
|
|
7
11
|
const COMPOSITE_KEY_SEP = "::";
|
|
12
|
+
/** 当前每个 session 的流式订阅(用于在 cancel 或新 run 前移除旧订阅,避免重复广播) */
|
|
13
|
+
const sessionSubscriptionBySessionId = new Map();
|
|
8
14
|
/**
|
|
9
|
-
*
|
|
15
|
+
* 移除某 session 的流式订阅(cancel 或新消息开始时调用,避免同一事件被多个回调处理导致界面重复)
|
|
16
|
+
*/
|
|
17
|
+
export function clearSessionStreamSubscription(sessionId) {
|
|
18
|
+
const unsub = sessionSubscriptionBySessionId.get(sessionId);
|
|
19
|
+
if (unsub) {
|
|
20
|
+
try {
|
|
21
|
+
unsub();
|
|
22
|
+
}
|
|
23
|
+
catch (_) { }
|
|
24
|
+
sessionSubscriptionBySessionId.delete(sessionId);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Broadcast message to all clients subscribed to a session (used by Web consumer).
|
|
10
29
|
*/
|
|
11
30
|
function broadcastToSession(sessionId, message) {
|
|
12
31
|
for (const client of connectedClients) {
|
|
@@ -15,6 +34,58 @@ function broadcastToSession(sessionId, message) {
|
|
|
15
34
|
}
|
|
16
35
|
}
|
|
17
36
|
}
|
|
37
|
+
/** 系统消息前缀,与正常回复同路下发,仅用此前缀区分,前端无需额外逻辑 */
|
|
38
|
+
const SYSTEM_MSG_PREFIX = "[System Message] ";
|
|
39
|
+
/** 系统消息结尾换行,与当前轮次助手内容分行显示 */
|
|
40
|
+
const SYSTEM_MSG_SUFFIX = "\n";
|
|
41
|
+
/**
|
|
42
|
+
* 创建 Web 端会话消息消费者:将统一出口的 SessionMessage 转为 Gateway 事件并 broadcast。
|
|
43
|
+
* 系统消息以 agent.chunk 形式发送,正文带 [System Message] 前缀且结尾换行,与当轮回复分行。
|
|
44
|
+
*/
|
|
45
|
+
function createWebSessionConsumer(_sessionId) {
|
|
46
|
+
return {
|
|
47
|
+
send(msg) {
|
|
48
|
+
const sid = msg.sessionId;
|
|
49
|
+
if (msg.type === "system" && msg.code === "command.result") {
|
|
50
|
+
const raw = msg.payload?.text ?? "";
|
|
51
|
+
const text = raw ? SYSTEM_MSG_PREFIX + raw + SYSTEM_MSG_SUFFIX : "";
|
|
52
|
+
if (text)
|
|
53
|
+
broadcastToSession(sid, createEvent("agent.chunk", { text, sessionId: sid }));
|
|
54
|
+
broadcastToSession(sid, createEvent("turn_end", { sessionId: sid, content: "" }));
|
|
55
|
+
broadcastToSession(sid, createEvent("message_complete", { sessionId: sid, content: "" }));
|
|
56
|
+
broadcastToSession(sid, createEvent("agent_end", { sessionId: sid }));
|
|
57
|
+
broadcastToSession(sid, createEvent("conversation_end", { sessionId: sid }));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (msg.type === "system" && msg.code === "mcp.progress") {
|
|
61
|
+
const raw = msg.payload?.message ?? msg.payload?.phase ?? "";
|
|
62
|
+
if (raw) {
|
|
63
|
+
const text = SYSTEM_MSG_PREFIX + raw + SYSTEM_MSG_SUFFIX;
|
|
64
|
+
broadcastToSession(sid, createEvent("agent.chunk", { text, sessionId: sid }));
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (msg.type === "chat") {
|
|
69
|
+
if (msg.code === "agent.chunk") {
|
|
70
|
+
broadcastToSession(sid, createEvent("agent.chunk", { ...msg.payload, sessionId: sid }));
|
|
71
|
+
}
|
|
72
|
+
else if (msg.code === "agent.tool") {
|
|
73
|
+
broadcastToSession(sid, createEvent("agent.tool", { ...msg.payload }));
|
|
74
|
+
}
|
|
75
|
+
else if (msg.code === "turn_end") {
|
|
76
|
+
broadcastToSession(sid, createEvent("turn_end", { sessionId: sid, ...msg.payload }));
|
|
77
|
+
broadcastToSession(sid, createEvent("message_complete", { sessionId: sid, ...msg.payload }));
|
|
78
|
+
}
|
|
79
|
+
else if (msg.code === "message_complete") {
|
|
80
|
+
broadcastToSession(sid, createEvent("message_complete", { sessionId: sid, ...msg.payload }));
|
|
81
|
+
}
|
|
82
|
+
else if (msg.code === "agent_end" || msg.code === "conversation_end") {
|
|
83
|
+
broadcastToSession(sid, createEvent(msg.code === "agent_end" ? "agent_end" : "conversation_end", { sessionId: sid, ...msg.payload }));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
18
89
|
/**
|
|
19
90
|
* Handle agent chat request with streaming support
|
|
20
91
|
*/
|
|
@@ -32,135 +103,255 @@ export async function handleAgentChat(client, params) {
|
|
|
32
103
|
return handleAgentChatInner(client, targetSessionId, message, params);
|
|
33
104
|
}
|
|
34
105
|
async function handleAgentChatInner(client, targetSessionId, message, params) {
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const storedAgentId = resolveCurrentAgent?.(targetSessionId);
|
|
40
|
-
const clientAgentId = params.agentId ?? client.agentId ?? "default";
|
|
41
|
-
let currentAgentId = params.agentId ?? storedAgentId ?? clientAgentId ?? "default";
|
|
42
|
-
if (params.agentId) {
|
|
43
|
-
getSessionCurrentAgentUpdater()?.(targetSessionId, params.agentId);
|
|
44
|
-
currentAgentId = params.agentId;
|
|
45
|
-
}
|
|
46
|
-
let workspace = "default";
|
|
47
|
-
let provider;
|
|
48
|
-
let modelId;
|
|
49
|
-
let apiKey;
|
|
50
|
-
const agentConfig = await loadDesktopAgentConfig(currentAgentId);
|
|
51
|
-
if (agentConfig) {
|
|
52
|
-
if (agentConfig.workspace)
|
|
53
|
-
workspace = agentConfig.workspace;
|
|
54
|
-
provider = agentConfig.provider;
|
|
55
|
-
modelId = agentConfig.model;
|
|
56
|
-
if (agentConfig.apiKey)
|
|
57
|
-
apiKey = agentConfig.apiKey;
|
|
58
|
-
}
|
|
59
|
-
const isEphemeralSession = sessionType === "system" || sessionType === "scheduled";
|
|
60
|
-
if (isEphemeralSession) {
|
|
61
|
-
agentManager.deleteSession(targetSessionId + COMPOSITE_KEY_SEP + currentAgentId);
|
|
62
|
-
}
|
|
63
|
-
const effectiveTargetAgentId = sessionType === "system" ? targetAgentId : currentAgentId;
|
|
64
|
-
const { maxAgentSessions } = getDesktopConfig();
|
|
65
|
-
let session;
|
|
106
|
+
const outlet = getSessionOutlet();
|
|
107
|
+
const unregisterConsumer = outlet
|
|
108
|
+
? outlet.registerConsumer(targetSessionId, createWebSessionConsumer(targetSessionId))
|
|
109
|
+
: () => { };
|
|
66
110
|
try {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
111
|
+
const { targetAgentId } = params;
|
|
112
|
+
const sessionType = params.sessionType ?? client.sessionType ?? "chat";
|
|
113
|
+
const resolveCurrentAgent = getSessionCurrentAgentResolver();
|
|
114
|
+
const storedAgentId = resolveCurrentAgent?.(targetSessionId);
|
|
115
|
+
const clientAgentId = params.agentId ?? client.agentId ?? "default";
|
|
116
|
+
const initialAgentId = params.agentId ?? storedAgentId ?? clientAgentId ?? "default";
|
|
117
|
+
const { message: preprocessedMessage, agentId: preprocessedAgentId, directResponse } = await preprocessInboundMessage({
|
|
118
|
+
sessionId: targetSessionId,
|
|
119
|
+
message: message.trim(),
|
|
120
|
+
currentAgentId: initialAgentId,
|
|
77
121
|
});
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
122
|
+
getSessionCurrentAgentUpdater()?.(targetSessionId, preprocessedAgentId);
|
|
123
|
+
if (directResponse != null && directResponse !== "") {
|
|
124
|
+
sendSessionMessage(targetSessionId, {
|
|
125
|
+
type: "system",
|
|
126
|
+
code: "command.result",
|
|
127
|
+
payload: { text: directResponse },
|
|
128
|
+
});
|
|
129
|
+
return { status: "completed", sessionId: targetSessionId };
|
|
84
130
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
let
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
131
|
+
message = preprocessedMessage;
|
|
132
|
+
let currentAgentId = preprocessedAgentId;
|
|
133
|
+
console.log(`[agent.chat] session=${targetSessionId} resolved agentId=${currentAgentId} (params=${params.agentId ?? "—"}, stored=${storedAgentId ?? "—"}, client=${client.agentId ?? "—"})`);
|
|
134
|
+
let workspace = "default";
|
|
135
|
+
let provider;
|
|
136
|
+
let modelId;
|
|
137
|
+
let apiKey;
|
|
138
|
+
const agentConfig = await loadDesktopAgentConfig(currentAgentId);
|
|
139
|
+
if (agentConfig) {
|
|
140
|
+
if (agentConfig.workspace)
|
|
141
|
+
workspace = agentConfig.workspace;
|
|
142
|
+
provider = agentConfig.provider;
|
|
143
|
+
modelId = agentConfig.model;
|
|
144
|
+
if (agentConfig.apiKey)
|
|
145
|
+
apiKey = agentConfig.apiKey;
|
|
146
|
+
}
|
|
147
|
+
const runnerType = agentConfig?.runnerType ?? "local";
|
|
148
|
+
const isProxyAgent = runnerType === "coze" || runnerType === "openclawx" || runnerType === "opencode";
|
|
149
|
+
if (isProxyAgent) {
|
|
150
|
+
console.log(`[agent.chat] Using proxy agent (${runnerType}) for session=${targetSessionId}, agentId=${currentAgentId}`);
|
|
151
|
+
}
|
|
152
|
+
// 代理智能体(Coze / OpenBot / OpenCode):走 AgentProxy 统一入口,流式结果经统一出口推给客户端
|
|
153
|
+
if (isProxyAgent) {
|
|
154
|
+
const { signal, unregister } = registerProxyRunAbort(targetSessionId, currentAgentId);
|
|
155
|
+
const finishAndUnregister = () => {
|
|
156
|
+
unregister();
|
|
157
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "turn_end", payload: {} });
|
|
158
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "message_complete", payload: {} });
|
|
159
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "agent_end", payload: {} });
|
|
160
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "conversation_end", payload: {} });
|
|
161
|
+
};
|
|
162
|
+
try {
|
|
163
|
+
await runForChannelStream({
|
|
164
|
+
sessionId: targetSessionId,
|
|
165
|
+
message,
|
|
166
|
+
agentId: currentAgentId,
|
|
167
|
+
signal,
|
|
168
|
+
}, {
|
|
169
|
+
onChunk(delta) {
|
|
170
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "agent.chunk", payload: { text: delta } });
|
|
171
|
+
},
|
|
172
|
+
onTurnEnd() {
|
|
173
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "turn_end", payload: {} });
|
|
174
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "message_complete", payload: {} });
|
|
175
|
+
},
|
|
176
|
+
onDone() {
|
|
177
|
+
finishAndUnregister();
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
return { status: "completed", sessionId: targetSessionId };
|
|
95
181
|
}
|
|
96
|
-
|
|
97
|
-
|
|
182
|
+
catch (error) {
|
|
183
|
+
const isAbort = error?.name === "AbortError" || (typeof error?.message === "string" && error.message.includes("abort"));
|
|
184
|
+
if (!isAbort)
|
|
185
|
+
console.error(`Error in agent chat (proxy ${runnerType}):`, error);
|
|
186
|
+
finishAndUnregister();
|
|
187
|
+
if (!isAbort) {
|
|
188
|
+
const errMsg = error?.message || String(error);
|
|
189
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "agent.chunk", payload: { text: `请求失败:${errMsg}` } });
|
|
190
|
+
}
|
|
191
|
+
return { status: "completed", sessionId: targetSessionId };
|
|
98
192
|
}
|
|
99
193
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
toolCallId: event.toolCallId,
|
|
104
|
-
toolName: event.toolName,
|
|
105
|
-
args: event.args
|
|
106
|
-
});
|
|
194
|
+
const isEphemeralSession = sessionType === "system" || sessionType === "scheduled";
|
|
195
|
+
if (isEphemeralSession) {
|
|
196
|
+
await agentManager.deleteSession(targetSessionId + COMPOSITE_KEY_SEP + currentAgentId);
|
|
107
197
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
toolName: event.toolName,
|
|
113
|
-
result: event.result,
|
|
114
|
-
isError: event.isError
|
|
115
|
-
});
|
|
198
|
+
const effectiveTargetAgentId = sessionType === "system" ? targetAgentId : currentAgentId;
|
|
199
|
+
const { maxAgentSessions } = getDesktopConfig();
|
|
200
|
+
if (await consumePendingAgentReload(currentAgentId)) {
|
|
201
|
+
await agentManager.deleteSessionsByAgentId(currentAgentId);
|
|
116
202
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
wsMessage = null;
|
|
203
|
+
let session;
|
|
204
|
+
try {
|
|
205
|
+
session = await agentManager.getOrCreateSession(targetSessionId, {
|
|
206
|
+
agentId: currentAgentId,
|
|
207
|
+
workspace,
|
|
208
|
+
provider,
|
|
209
|
+
modelId,
|
|
210
|
+
apiKey,
|
|
211
|
+
maxSessions: maxAgentSessions,
|
|
212
|
+
targetAgentId: effectiveTargetAgentId,
|
|
213
|
+
mcpServers: agentConfig?.mcpServers,
|
|
214
|
+
systemPrompt: agentConfig?.systemPrompt,
|
|
215
|
+
useLongMemory: agentConfig?.useLongMemory,
|
|
216
|
+
});
|
|
132
217
|
}
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
218
|
+
catch (err) {
|
|
219
|
+
const msg = err?.message ?? String(err);
|
|
220
|
+
if (msg.includes("No API key") || msg.includes("API key")) {
|
|
221
|
+
const prov = provider ?? "deepseek";
|
|
222
|
+
throw new Error(`未配置 ${prov} 的 API Key。请在桌面端「设置」-「模型/API」中配置,或运行:openbot login ${prov} <你的API Key>`);
|
|
223
|
+
}
|
|
224
|
+
throw err;
|
|
138
225
|
}
|
|
139
|
-
|
|
140
|
-
|
|
226
|
+
// 向各通道广播:turn_end(本小轮结束)、agent_end(整轮对话结束),经统一出口推送给已注册消费者
|
|
227
|
+
let resolveAgentDone;
|
|
228
|
+
const agentDonePromise = new Promise((resolve) => {
|
|
229
|
+
resolveAgentDone = resolve;
|
|
230
|
+
});
|
|
231
|
+
let didUnsubscribe = false;
|
|
232
|
+
let unsubscribe;
|
|
233
|
+
const doUnsubscribe = () => {
|
|
234
|
+
if (didUnsubscribe)
|
|
235
|
+
return;
|
|
236
|
+
didUnsubscribe = true;
|
|
237
|
+
sessionSubscriptionBySessionId.delete(targetSessionId);
|
|
238
|
+
unsubscribe();
|
|
239
|
+
};
|
|
240
|
+
clearSessionStreamSubscription(targetSessionId);
|
|
241
|
+
let hasReceivedAnyChunk = false;
|
|
242
|
+
unsubscribe = session.subscribe((event) => {
|
|
243
|
+
if (event.type !== "message_update") {
|
|
244
|
+
console.log(`[agent.chat] event: ${event.type}`);
|
|
245
|
+
}
|
|
246
|
+
let wsPayload = null;
|
|
247
|
+
if (event.type === "message_update") {
|
|
248
|
+
const update = event;
|
|
249
|
+
if (update.assistantMessageEvent && update.assistantMessageEvent.type === "text_delta") {
|
|
250
|
+
hasReceivedAnyChunk = true;
|
|
251
|
+
wsPayload = { type: "chat", code: "agent.chunk", payload: { text: update.assistantMessageEvent.delta } };
|
|
252
|
+
}
|
|
253
|
+
else if (update.assistantMessageEvent && update.assistantMessageEvent.type === "thinking_delta") {
|
|
254
|
+
wsPayload = { type: "chat", code: "agent.chunk", payload: { text: update.assistantMessageEvent.delta, isThinking: true } };
|
|
255
|
+
}
|
|
256
|
+
else if (update.assistantMessageEvent?.type === "error" && update.assistantMessageEvent?.error?.errorMessage) {
|
|
257
|
+
console.warn("[agent.chat] model error:", update.assistantMessageEvent.error.errorMessage);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else if (event.type === "tool_execution_start") {
|
|
261
|
+
wsPayload = { type: "chat", code: "agent.tool", payload: { type: "start", toolCallId: event.toolCallId, toolName: event.toolName, args: event.args } };
|
|
262
|
+
}
|
|
263
|
+
else if (event.type === "tool_execution_end") {
|
|
264
|
+
wsPayload = { type: "chat", code: "agent.tool", payload: { type: "end", toolCallId: event.toolCallId, toolName: event.toolName, result: event.result, isError: event.isError } };
|
|
265
|
+
}
|
|
266
|
+
else if (event.type === "message_end") {
|
|
267
|
+
const msg = event.message;
|
|
268
|
+
if (msg?.role === "assistant" && msg?.content && Array.isArray(msg.content)) {
|
|
269
|
+
const text = msg.content
|
|
270
|
+
.filter((c) => c?.type === "text" && typeof c.text === "string")
|
|
271
|
+
.map((c) => c.text)
|
|
272
|
+
.join("");
|
|
273
|
+
if (text && !hasReceivedAnyChunk) {
|
|
274
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "agent.chunk", payload: { text } });
|
|
275
|
+
}
|
|
276
|
+
hasReceivedAnyChunk = true;
|
|
277
|
+
}
|
|
278
|
+
if (msg?.errorMessage) {
|
|
279
|
+
const errText = msg.errorMessage.includes("402") || msg.errorMessage.includes("Insufficient Balance")
|
|
280
|
+
? "API 余额不足,请到「设置」检查并充值后重试。"
|
|
281
|
+
: `请求失败:${msg.errorMessage}`;
|
|
282
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "agent.chunk", payload: { text: errText } });
|
|
283
|
+
}
|
|
284
|
+
wsPayload = null;
|
|
285
|
+
}
|
|
286
|
+
else if (event.type === "turn_end") {
|
|
287
|
+
const msg = event.message;
|
|
288
|
+
if (!hasReceivedAnyChunk && msg?.content && Array.isArray(msg.content)) {
|
|
289
|
+
const fullText = msg.content
|
|
290
|
+
.filter((c) => c?.type === "text" && typeof c.text === "string")
|
|
291
|
+
.map((c) => c.text)
|
|
292
|
+
.join("");
|
|
293
|
+
if (fullText) {
|
|
294
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "agent.chunk", payload: { text: fullText } });
|
|
295
|
+
hasReceivedAnyChunk = true;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (msg?.errorMessage) {
|
|
299
|
+
const errText = msg.errorMessage.includes("402") || msg.errorMessage.includes("Insufficient Balance")
|
|
300
|
+
? "API 余额不足,请到「设置」检查并充值后重试。"
|
|
301
|
+
: `请求失败:${msg.errorMessage}`;
|
|
302
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "agent.chunk", payload: { text: errText } });
|
|
303
|
+
hasReceivedAnyChunk = true;
|
|
304
|
+
}
|
|
305
|
+
const usage = msg?.usage;
|
|
306
|
+
const promptTokens = Number(usage?.input ?? usage?.input_tokens ?? 0) || 0;
|
|
307
|
+
const completionTokens = Number(usage?.output ?? usage?.output_tokens ?? 0) || 0;
|
|
308
|
+
const usagePayload = promptTokens > 0 || completionTokens > 0 ? { promptTokens, completionTokens } : undefined;
|
|
309
|
+
const turnPayload = { content: "", ...(usagePayload && { usage: usagePayload }) };
|
|
310
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "turn_end", payload: turnPayload });
|
|
311
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "message_complete", payload: turnPayload });
|
|
312
|
+
wsPayload = null;
|
|
313
|
+
}
|
|
314
|
+
else if (event.type === "agent_end") {
|
|
315
|
+
if (!hasReceivedAnyChunk && Array.isArray(event.messages)) {
|
|
316
|
+
const messages = event.messages;
|
|
317
|
+
const lastAssistant = [...messages].reverse().find((m) => m?.role === "assistant" && m?.content);
|
|
318
|
+
if (lastAssistant?.content) {
|
|
319
|
+
const text = lastAssistant.content
|
|
320
|
+
.filter((c) => c?.type === "text" && typeof c.text === "string")
|
|
321
|
+
.map((c) => c.text)
|
|
322
|
+
.join("");
|
|
323
|
+
if (text)
|
|
324
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "agent.chunk", payload: { text } });
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "agent_end", payload: {} });
|
|
328
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "conversation_end", payload: {} });
|
|
329
|
+
wsPayload = null;
|
|
330
|
+
resolveAgentDone();
|
|
331
|
+
doUnsubscribe();
|
|
332
|
+
if (isEphemeralSession) {
|
|
333
|
+
void agentManager.deleteSession(targetSessionId + COMPOSITE_KEY_SEP + currentAgentId).catch(() => { });
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (wsPayload) {
|
|
337
|
+
sendSessionMessage(targetSessionId, wsPayload);
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
sessionSubscriptionBySessionId.set(targetSessionId, unsubscribe);
|
|
341
|
+
try {
|
|
342
|
+
await session.sendUserMessage(message, { deliverAs: "followUp" });
|
|
343
|
+
await agentDonePromise;
|
|
344
|
+
console.log(`Agent chat completed for session ${targetSessionId}`);
|
|
345
|
+
return { status: "completed", sessionId: targetSessionId };
|
|
141
346
|
}
|
|
142
|
-
|
|
143
|
-
|
|
347
|
+
catch (error) {
|
|
348
|
+
console.error(`Error in agent chat:`, error);
|
|
349
|
+
resolveAgentDone();
|
|
350
|
+
doUnsubscribe();
|
|
351
|
+
throw error;
|
|
144
352
|
}
|
|
145
|
-
});
|
|
146
|
-
try {
|
|
147
|
-
const experienceBlock = await getExperienceContextForUserMessage();
|
|
148
|
-
const userMessageToSend = experienceBlock.trim().length > 0
|
|
149
|
-
? `${experienceBlock}\n\n用户问题:\n${message}`
|
|
150
|
-
: message;
|
|
151
|
-
// 若 agent 正在流式输出,deliverAs: 'followUp' 将本条消息排队,避免抛出 "Agent is already processing"
|
|
152
|
-
await session.sendUserMessage(userMessageToSend, { deliverAs: "followUp" });
|
|
153
|
-
console.log(`Agent chat completed for session ${targetSessionId}`);
|
|
154
|
-
return {
|
|
155
|
-
status: "completed",
|
|
156
|
-
sessionId: targetSessionId,
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
catch (error) {
|
|
160
|
-
console.error(`Error in agent chat:`, error);
|
|
161
|
-
throw error;
|
|
162
353
|
}
|
|
163
354
|
finally {
|
|
164
|
-
|
|
355
|
+
unregisterConsumer();
|
|
165
356
|
}
|
|
166
357
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { agentManager } from "../../core/agent/agent-manager.js";
|
|
2
|
-
import { getExperienceContextForUserMessage } from "../../core/memory/index.js";
|
|
3
2
|
import { loadDesktopAgentConfig } from "../../core/config/desktop-config.js";
|
|
3
|
+
import { consumePendingAgentReload } from "../../core/config/agent-reload-pending.js";
|
|
4
4
|
async function readBody(req) {
|
|
5
5
|
return new Promise((resolve, reject) => {
|
|
6
6
|
const chunks = [];
|
|
@@ -50,6 +50,9 @@ export async function handleRunScheduledTask(req, res) {
|
|
|
50
50
|
apiKey = agentConfig.apiKey;
|
|
51
51
|
}
|
|
52
52
|
const COMPOSITE_KEY_SEP = "::";
|
|
53
|
+
if (await consumePendingAgentReload(sessionAgentId)) {
|
|
54
|
+
await agentManager.deleteSessionsByAgentId(sessionAgentId);
|
|
55
|
+
}
|
|
53
56
|
try {
|
|
54
57
|
const session = await agentManager.getOrCreateSession(sessionId, {
|
|
55
58
|
agentId: sessionAgentId,
|
|
@@ -59,6 +62,7 @@ export async function handleRunScheduledTask(req, res) {
|
|
|
59
62
|
apiKey,
|
|
60
63
|
mcpServers: agentConfig?.mcpServers,
|
|
61
64
|
systemPrompt: agentConfig?.systemPrompt,
|
|
65
|
+
useLongMemory: agentConfig?.useLongMemory,
|
|
62
66
|
});
|
|
63
67
|
let assistantContent = "";
|
|
64
68
|
let turnPromptTokens = 0;
|
|
@@ -78,10 +82,6 @@ export async function handleRunScheduledTask(req, res) {
|
|
|
78
82
|
}
|
|
79
83
|
}
|
|
80
84
|
});
|
|
81
|
-
const experienceBlock = await getExperienceContextForUserMessage();
|
|
82
|
-
const userMessageToSend = experienceBlock.trim().length > 0
|
|
83
|
-
? `${experienceBlock}\n\n用户问题:\n${message}`
|
|
84
|
-
: message;
|
|
85
85
|
// 定时任务复用同一 session:若上次执行未结束会报 "Agent is already processing"。先等待空闲再发,避免并发。
|
|
86
86
|
const idleTimeoutMs = 10 * 60 * 1000;
|
|
87
87
|
const pollMs = 2000;
|
|
@@ -93,7 +93,7 @@ export async function handleRunScheduledTask(req, res) {
|
|
|
93
93
|
if (session.isStreaming) {
|
|
94
94
|
throw new Error("Session still busy after waiting; try again later.");
|
|
95
95
|
}
|
|
96
|
-
await session.sendUserMessage(
|
|
96
|
+
await session.sendUserMessage(message);
|
|
97
97
|
unsubscribe();
|
|
98
98
|
if (backendBaseUrl && assistantContent !== undefined) {
|
|
99
99
|
const url = `${backendBaseUrl.replace(/\/$/, "")}/server-api/agents/sessions/${encodeURIComponent(sessionId)}/messages`;
|
|
@@ -124,6 +124,6 @@ export async function handleRunScheduledTask(req, res) {
|
|
|
124
124
|
res.end(JSON.stringify({ success: false, error: friendlyError }));
|
|
125
125
|
}
|
|
126
126
|
finally {
|
|
127
|
-
agentManager.deleteSession(sessionId + COMPOSITE_KEY_SEP + sessionAgentId);
|
|
127
|
+
await agentManager.deleteSession(sessionId + COMPOSITE_KEY_SEP + sessionAgentId);
|
|
128
128
|
}
|
|
129
129
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function registerProxyRunAbort(sessionId: string, agentId: string): {
|
|
2
|
+
signal: AbortSignal;
|
|
3
|
+
unregister: () => void;
|
|
4
|
+
};
|
|
5
|
+
/** Abort the proxy run for this session+agent if any; returns true if aborted. */
|
|
6
|
+
export declare function abortProxyRun(sessionId: string, agentId: string): boolean;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry of AbortControllers for in-flight proxy runs (Coze/OpenBot/OpenCode).
|
|
3
|
+
* agent.cancel looks up by sessionId+agentId and aborts the controller so the run stops.
|
|
4
|
+
*/
|
|
5
|
+
const SEP = "::";
|
|
6
|
+
function key(sessionId, agentId) {
|
|
7
|
+
return sessionId + SEP + agentId;
|
|
8
|
+
}
|
|
9
|
+
const controllers = new Map();
|
|
10
|
+
export function registerProxyRunAbort(sessionId, agentId) {
|
|
11
|
+
const k = key(sessionId, agentId);
|
|
12
|
+
const existing = controllers.get(k);
|
|
13
|
+
if (existing) {
|
|
14
|
+
existing.abort();
|
|
15
|
+
controllers.delete(k);
|
|
16
|
+
}
|
|
17
|
+
const controller = new AbortController();
|
|
18
|
+
controllers.set(k, controller);
|
|
19
|
+
return {
|
|
20
|
+
signal: controller.signal,
|
|
21
|
+
unregister: () => {
|
|
22
|
+
controllers.delete(k);
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** Abort the proxy run for this session+agent if any; returns true if aborted. */
|
|
27
|
+
export function abortProxyRun(sessionId, agentId) {
|
|
28
|
+
const k = key(sessionId, agentId);
|
|
29
|
+
const c = controllers.get(k);
|
|
30
|
+
if (!c)
|
|
31
|
+
return false;
|
|
32
|
+
try {
|
|
33
|
+
c.abort();
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
controllers.delete(k);
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
}
|