@next-open-ai/openbot 0.6.8 → 0.6.66
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 +155 -136
- package/apps/desktop/renderer/dist/assets/index-CxDZnMBH.css +10 -0
- package/apps/desktop/renderer/dist/assets/index-k47Qiokg.js +93 -0
- package/apps/desktop/renderer/dist/index.html +2 -2
- package/dist/cli/cli.js +136 -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 +27 -6
- package/dist/core/agent/agent-manager.js +147 -26
- 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/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 +95 -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 +786 -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 +95 -0
- package/dist/core/agent/proxy/index.d.ts +3 -0
- package/dist/core/agent/proxy/index.js +18 -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/agent/token-usage-log-extension.d.ts +14 -0
- package/dist/core/agent/token-usage-log-extension.js +61 -0
- package/dist/core/config/agent-reload-pending.d.ts +9 -0
- package/dist/core/config/agent-reload-pending.js +67 -0
- package/dist/core/config/desktop-config.d.ts +136 -5
- package/dist/core/config/desktop-config.js +470 -46
- package/dist/core/config/provider-support-default.js +27 -0
- 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/inbound-message-preprocess.d.ts +27 -0
- package/dist/core/inbound-message-preprocess.js +96 -0
- package/dist/core/local-llm-server/download-model.d.ts +16 -0
- package/dist/core/local-llm-server/download-model.js +37 -0
- package/dist/core/local-llm-server/index.d.ts +32 -0
- package/dist/core/local-llm-server/index.js +152 -0
- package/dist/core/local-llm-server/llm-context.d.ts +66 -0
- package/dist/core/local-llm-server/llm-context.js +270 -0
- package/dist/core/local-llm-server/model-resolve.d.ts +27 -0
- package/dist/core/local-llm-server/model-resolve.js +90 -0
- package/dist/core/local-llm-server/server.d.ts +1 -0
- package/dist/core/local-llm-server/server.js +234 -0
- package/dist/core/local-llm-server/start-from-config.d.ts +5 -0
- package/dist/core/local-llm-server/start-from-config.js +50 -0
- package/dist/core/mcp/adapter.d.ts +4 -2
- package/dist/core/mcp/adapter.js +10 -4
- 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 +10 -6
- package/dist/core/mcp/index.js +7 -3
- package/dist/core/mcp/operator.d.ts +28 -2
- package/dist/core/mcp/operator.js +131 -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 +12 -0
- package/dist/core/mcp/transport/stdio.js +147 -29
- 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 +78 -0
- package/dist/core/memory/local-embedding.d.ts +11 -0
- package/dist/core/memory/local-embedding.js +69 -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 +3 -1
- package/dist/core/tools/index.js +3 -1
- package/dist/core/tools/memory-recall-tool.d.ts +6 -0
- package/dist/core/tools/memory-recall-tool.js +77 -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/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 +377 -118
- package/dist/gateway/methods/run-scheduled-task.js +9 -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 +123 -19
- package/dist/server/agent-config/agent-config.controller.d.ts +10 -2
- package/dist/server/agent-config/agent-config.controller.js +19 -4
- package/dist/server/agent-config/agent-config.module.js +3 -1
- package/dist/server/agent-config/agent-config.service.d.ts +91 -6
- package/dist/server/agent-config/agent-config.service.js +115 -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.gateway.js +1 -1
- package/dist/server/agents/agents.service.js +1 -1
- package/dist/server/bootstrap.d.ts +1 -0
- package/dist/server/bootstrap.js +28 -4
- package/dist/server/config/config.controller.d.ts +134 -2
- package/dist/server/config/config.controller.js +199 -3
- package/dist/server/config/config.module.js +5 -4
- package/dist/server/config/config.service.d.ts +32 -2
- package/dist/server/config/config.service.js +69 -9
- package/dist/server/config/local-models.service.d.ts +67 -0
- package/dist/server/config/local-models.service.js +242 -0
- package/dist/server/workspace/workspace.service.d.ts +7 -0
- package/dist/server/workspace/workspace.service.js +16 -0
- package/package.json +10 -2
- package/presets/preset-agents.json +128 -0
- package/presets/preset-config.json +29 -0
- package/presets/preset-providers.json +180 -0
- package/presets/recommended-local-models.json +36 -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/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/skills/url-bookmark/SKILL.md +12 -12
- 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,48 @@
|
|
|
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
|
+
/** 将 delta/text 规范为字符串,避免 SDK 或上游返回对象时前端显示 [object Object] 或触发 Unknown value type */
|
|
13
|
+
function normalizeChunkText(v) {
|
|
14
|
+
if (v == null)
|
|
15
|
+
return "";
|
|
16
|
+
if (typeof v === "string")
|
|
17
|
+
return v;
|
|
18
|
+
if (typeof v.content === "string")
|
|
19
|
+
return v.content;
|
|
20
|
+
if (typeof v.text === "string")
|
|
21
|
+
return v.text;
|
|
22
|
+
try {
|
|
23
|
+
return String(JSON.stringify(v));
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return String(v);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** 当前每个 session 的流式订阅(用于在 cancel 或新 run 前移除旧订阅,避免重复广播) */
|
|
30
|
+
const sessionSubscriptionBySessionId = new Map();
|
|
31
|
+
/**
|
|
32
|
+
* 移除某 session 的流式订阅(cancel 或新消息开始时调用,避免同一事件被多个回调处理导致界面重复)
|
|
33
|
+
*/
|
|
34
|
+
export function clearSessionStreamSubscription(sessionId) {
|
|
35
|
+
const unsub = sessionSubscriptionBySessionId.get(sessionId);
|
|
36
|
+
if (unsub) {
|
|
37
|
+
try {
|
|
38
|
+
unsub();
|
|
39
|
+
}
|
|
40
|
+
catch (_) { }
|
|
41
|
+
sessionSubscriptionBySessionId.delete(sessionId);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
8
44
|
/**
|
|
9
|
-
* Broadcast message to all clients subscribed to a session
|
|
45
|
+
* Broadcast message to all clients subscribed to a session (used by Web consumer).
|
|
10
46
|
*/
|
|
11
47
|
function broadcastToSession(sessionId, message) {
|
|
12
48
|
for (const client of connectedClients) {
|
|
@@ -15,6 +51,55 @@ function broadcastToSession(sessionId, message) {
|
|
|
15
51
|
}
|
|
16
52
|
}
|
|
17
53
|
}
|
|
54
|
+
/** 系统消息前缀,与正常回复同路下发,仅用此前缀区分,前端无需额外逻辑 */
|
|
55
|
+
const SYSTEM_MSG_PREFIX = "[System Message] ";
|
|
56
|
+
/** 系统消息结尾换行,与当前轮次助手内容分行显示 */
|
|
57
|
+
const SYSTEM_MSG_SUFFIX = "\n";
|
|
58
|
+
/**
|
|
59
|
+
* 创建 Web 端会话消息消费者:将统一出口的 SessionMessage 转为 Gateway 事件并 broadcast。
|
|
60
|
+
* 系统消息以独立事件 system_message 下发,前端做中间展示、不进入 session 聊天记录;各通道通过统一出口收到原始 system 消息后自行处理。
|
|
61
|
+
*/
|
|
62
|
+
function createWebSessionConsumer(_sessionId) {
|
|
63
|
+
return {
|
|
64
|
+
send(msg) {
|
|
65
|
+
const sid = msg.sessionId;
|
|
66
|
+
if (msg.type === "system" && msg.code === "command.result") {
|
|
67
|
+
const raw = msg.payload?.text ?? "";
|
|
68
|
+
if (raw)
|
|
69
|
+
broadcastToSession(sid, createEvent("system_message", { text: raw, code: "command.result", sessionId: sid }));
|
|
70
|
+
broadcastToSession(sid, createEvent("turn_end", { sessionId: sid, content: "" }));
|
|
71
|
+
broadcastToSession(sid, createEvent("message_complete", { sessionId: sid, content: "" }));
|
|
72
|
+
broadcastToSession(sid, createEvent("agent_end", { sessionId: sid }));
|
|
73
|
+
broadcastToSession(sid, createEvent("conversation_end", { sessionId: sid }));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (msg.type === "system" && msg.code === "mcp.progress") {
|
|
77
|
+
const raw = msg.payload?.message ?? msg.payload?.phase ?? "";
|
|
78
|
+
if (raw)
|
|
79
|
+
broadcastToSession(sid, createEvent("system_message", { text: raw, code: "mcp.progress", sessionId: sid }));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (msg.type === "chat") {
|
|
83
|
+
if (msg.code === "agent.chunk") {
|
|
84
|
+
broadcastToSession(sid, createEvent("agent.chunk", { ...msg.payload, sessionId: sid }));
|
|
85
|
+
}
|
|
86
|
+
else if (msg.code === "agent.tool") {
|
|
87
|
+
broadcastToSession(sid, createEvent("agent.tool", { ...msg.payload }));
|
|
88
|
+
}
|
|
89
|
+
else if (msg.code === "turn_end") {
|
|
90
|
+
broadcastToSession(sid, createEvent("turn_end", { sessionId: sid, ...msg.payload }));
|
|
91
|
+
broadcastToSession(sid, createEvent("message_complete", { sessionId: sid, ...msg.payload }));
|
|
92
|
+
}
|
|
93
|
+
else if (msg.code === "message_complete") {
|
|
94
|
+
broadcastToSession(sid, createEvent("message_complete", { sessionId: sid, ...msg.payload }));
|
|
95
|
+
}
|
|
96
|
+
else if (msg.code === "agent_end" || msg.code === "conversation_end") {
|
|
97
|
+
broadcastToSession(sid, createEvent(msg.code === "agent_end" ? "agent_end" : "conversation_end", { sessionId: sid, ...msg.payload }));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
18
103
|
/**
|
|
19
104
|
* Handle agent chat request with streaming support
|
|
20
105
|
*/
|
|
@@ -32,135 +117,309 @@ export async function handleAgentChat(client, params) {
|
|
|
32
117
|
return handleAgentChatInner(client, targetSessionId, message, params);
|
|
33
118
|
}
|
|
34
119
|
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;
|
|
120
|
+
const outlet = getSessionOutlet();
|
|
121
|
+
const unregisterConsumer = outlet
|
|
122
|
+
? outlet.registerConsumer(targetSessionId, createWebSessionConsumer(targetSessionId))
|
|
123
|
+
: () => { };
|
|
66
124
|
try {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
125
|
+
const { targetAgentId } = params;
|
|
126
|
+
const sessionType = params.sessionType ?? client.sessionType ?? "chat";
|
|
127
|
+
const resolveCurrentAgent = getSessionCurrentAgentResolver();
|
|
128
|
+
const storedAgentId = resolveCurrentAgent?.(targetSessionId);
|
|
129
|
+
const clientAgentId = params.agentId ?? client.agentId ?? "default";
|
|
130
|
+
const initialAgentId = params.agentId ?? storedAgentId ?? clientAgentId ?? "default";
|
|
131
|
+
const { message: preprocessedMessage, agentId: preprocessedAgentId, directResponse } = await preprocessInboundMessage({
|
|
132
|
+
sessionId: targetSessionId,
|
|
133
|
+
message: message.trim(),
|
|
134
|
+
currentAgentId: initialAgentId,
|
|
77
135
|
});
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
136
|
+
getSessionCurrentAgentUpdater()?.(targetSessionId, preprocessedAgentId);
|
|
137
|
+
if (directResponse != null && directResponse !== "") {
|
|
138
|
+
sendSessionMessage(targetSessionId, {
|
|
139
|
+
type: "system",
|
|
140
|
+
code: "command.result",
|
|
141
|
+
payload: { text: directResponse },
|
|
142
|
+
});
|
|
143
|
+
return { status: "completed", sessionId: targetSessionId };
|
|
84
144
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
let
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
145
|
+
message = preprocessedMessage;
|
|
146
|
+
let currentAgentId = preprocessedAgentId;
|
|
147
|
+
console.log(`[agent.chat] session=${targetSessionId} resolved agentId=${currentAgentId} (params=${params.agentId ?? "—"}, stored=${storedAgentId ?? "—"}, client=${client.agentId ?? "—"})`);
|
|
148
|
+
let workspace = "default";
|
|
149
|
+
let provider;
|
|
150
|
+
let modelId;
|
|
151
|
+
let apiKey;
|
|
152
|
+
const agentConfig = await loadDesktopAgentConfig(currentAgentId);
|
|
153
|
+
if (agentConfig) {
|
|
154
|
+
if (agentConfig.workspace)
|
|
155
|
+
workspace = agentConfig.workspace;
|
|
156
|
+
provider = agentConfig.provider;
|
|
157
|
+
modelId = agentConfig.model;
|
|
158
|
+
if (agentConfig.apiKey)
|
|
159
|
+
apiKey = agentConfig.apiKey;
|
|
99
160
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
toolName: event.toolName,
|
|
105
|
-
args: event.args
|
|
106
|
-
});
|
|
161
|
+
const runnerType = agentConfig?.runnerType ?? "local";
|
|
162
|
+
const isProxyAgent = runnerType === "coze" || runnerType === "openclawx" || runnerType === "opencode" || runnerType === "claude_code";
|
|
163
|
+
if (isProxyAgent) {
|
|
164
|
+
console.log(`[agent.chat] Using proxy agent (${runnerType}) for session=${targetSessionId}, agentId=${currentAgentId}`);
|
|
107
165
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
166
|
+
// 代理智能体(Coze / OpenBot / OpenCode):走 AgentProxy 统一入口,流式结果经统一出口推给客户端
|
|
167
|
+
if (isProxyAgent) {
|
|
168
|
+
const { signal, unregister } = registerProxyRunAbort(targetSessionId, currentAgentId);
|
|
169
|
+
const finishAndUnregister = () => {
|
|
170
|
+
unregister();
|
|
171
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "turn_end", payload: {} });
|
|
172
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "message_complete", payload: {} });
|
|
173
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "agent_end", payload: {} });
|
|
174
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "conversation_end", payload: {} });
|
|
175
|
+
};
|
|
176
|
+
runForChannelStream({
|
|
177
|
+
sessionId: targetSessionId,
|
|
178
|
+
message,
|
|
179
|
+
agentId: currentAgentId,
|
|
180
|
+
signal,
|
|
181
|
+
}, {
|
|
182
|
+
onChunk(delta) {
|
|
183
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "agent.chunk", payload: { text: delta } });
|
|
184
|
+
},
|
|
185
|
+
onTurnEnd() {
|
|
186
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "turn_end", payload: {} });
|
|
187
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "message_complete", payload: {} });
|
|
188
|
+
},
|
|
189
|
+
onDone() {
|
|
190
|
+
finishAndUnregister();
|
|
191
|
+
},
|
|
192
|
+
}).catch((error) => {
|
|
193
|
+
const isAbort = error?.name === "AbortError" || (typeof error?.message === "string" && error.message.includes("abort"));
|
|
194
|
+
if (!isAbort)
|
|
195
|
+
console.error(`Error in agent chat (proxy ${runnerType}):`, error);
|
|
196
|
+
finishAndUnregister();
|
|
197
|
+
if (!isAbort) {
|
|
198
|
+
let errMsg = error?.message || String(error);
|
|
199
|
+
const needNormalize = typeof errMsg === "object" || (typeof errMsg === "string" && errMsg.includes("[object Object]"));
|
|
200
|
+
if (needNormalize) {
|
|
201
|
+
errMsg = normalizeChunkText(errMsg);
|
|
202
|
+
if (typeof errMsg === "string" && errMsg.includes("Unknown value type") && errMsg.includes("[object Object]")) {
|
|
203
|
+
errMsg = "模型返回了不支持的数据结构(如工具调用流),请尝试关闭工具或更换模型。";
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "agent.chunk", payload: { text: `请求失败:${errMsg}` } });
|
|
207
|
+
}
|
|
115
208
|
});
|
|
209
|
+
return { status: "streaming", sessionId: targetSessionId };
|
|
116
210
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const completionTokens = Number(usage?.output ?? usage?.output_tokens ?? 0) || 0;
|
|
121
|
-
const usagePayload = promptTokens > 0 || completionTokens > 0
|
|
122
|
-
? { promptTokens, completionTokens }
|
|
123
|
-
: undefined;
|
|
124
|
-
const turnPayload = {
|
|
125
|
-
sessionId: targetSessionId,
|
|
126
|
-
content: "",
|
|
127
|
-
...(usagePayload && { usage: usagePayload }),
|
|
128
|
-
};
|
|
129
|
-
broadcastToSession(targetSessionId, createEvent("turn_end", turnPayload));
|
|
130
|
-
broadcastToSession(targetSessionId, createEvent("message_complete", turnPayload));
|
|
131
|
-
wsMessage = null;
|
|
211
|
+
const isEphemeralSession = sessionType === "system" || sessionType === "scheduled";
|
|
212
|
+
if (isEphemeralSession) {
|
|
213
|
+
await agentManager.deleteSession(targetSessionId + COMPOSITE_KEY_SEP + currentAgentId);
|
|
132
214
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
wsMessage = null;
|
|
215
|
+
const effectiveTargetAgentId = sessionType === "system" ? targetAgentId : currentAgentId;
|
|
216
|
+
const { maxAgentSessions } = getDesktopConfig();
|
|
217
|
+
if (await consumePendingAgentReload(currentAgentId)) {
|
|
218
|
+
await agentManager.deleteSessionsByAgentId(currentAgentId);
|
|
138
219
|
}
|
|
139
|
-
|
|
140
|
-
|
|
220
|
+
let session;
|
|
221
|
+
try {
|
|
222
|
+
session = await agentManager.getOrCreateSession(targetSessionId, {
|
|
223
|
+
agentId: currentAgentId,
|
|
224
|
+
workspace,
|
|
225
|
+
provider,
|
|
226
|
+
modelId,
|
|
227
|
+
apiKey,
|
|
228
|
+
maxSessions: maxAgentSessions,
|
|
229
|
+
targetAgentId: effectiveTargetAgentId,
|
|
230
|
+
mcpServers: agentConfig?.mcpServers,
|
|
231
|
+
mcpMaxResultTokens: agentConfig?.mcpMaxResultTokens,
|
|
232
|
+
systemPrompt: agentConfig?.systemPrompt,
|
|
233
|
+
useLongMemory: agentConfig?.useLongMemory,
|
|
234
|
+
webSearch: agentConfig?.webSearch,
|
|
235
|
+
});
|
|
141
236
|
}
|
|
142
|
-
|
|
143
|
-
|
|
237
|
+
catch (err) {
|
|
238
|
+
const msg = err?.message ?? String(err);
|
|
239
|
+
if (msg.includes("No API key") || msg.includes("API key")) {
|
|
240
|
+
const prov = provider ?? "deepseek";
|
|
241
|
+
throw new Error(`未配置 ${prov} 的 API Key。请在桌面端「设置」-「模型/API」中配置,或运行:openbot login ${prov} <你的API Key>`);
|
|
242
|
+
}
|
|
243
|
+
throw err;
|
|
144
244
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
245
|
+
// 向各通道广播:turn_end(本小轮结束)、agent_end(整轮对话结束),经统一出口推送给已注册消费者
|
|
246
|
+
let resolveAgentDone;
|
|
247
|
+
const agentDonePromise = new Promise((resolve) => {
|
|
248
|
+
resolveAgentDone = resolve;
|
|
249
|
+
});
|
|
250
|
+
let didUnsubscribe = false;
|
|
251
|
+
let unsubscribe;
|
|
252
|
+
const doUnsubscribe = () => {
|
|
253
|
+
if (didUnsubscribe)
|
|
254
|
+
return;
|
|
255
|
+
didUnsubscribe = true;
|
|
256
|
+
sessionSubscriptionBySessionId.delete(targetSessionId);
|
|
257
|
+
unsubscribe();
|
|
157
258
|
};
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
259
|
+
clearSessionStreamSubscription(targetSessionId);
|
|
260
|
+
let hasReceivedAnyChunk = false;
|
|
261
|
+
unsubscribe = session.subscribe((event) => {
|
|
262
|
+
if (event.type !== "message_update") {
|
|
263
|
+
console.log(`[agent.chat] event: ${event.type}`);
|
|
264
|
+
}
|
|
265
|
+
let wsPayload = null;
|
|
266
|
+
if (event.type === "message_update") {
|
|
267
|
+
const update = event;
|
|
268
|
+
if (update.assistantMessageEvent && update.assistantMessageEvent.type === "text_delta") {
|
|
269
|
+
hasReceivedAnyChunk = true;
|
|
270
|
+
wsPayload = { type: "chat", code: "agent.chunk", payload: { text: normalizeChunkText(update.assistantMessageEvent.delta) } };
|
|
271
|
+
}
|
|
272
|
+
else if (update.assistantMessageEvent && update.assistantMessageEvent.type === "thinking_delta") {
|
|
273
|
+
wsPayload = { type: "chat", code: "agent.chunk", payload: { text: normalizeChunkText(update.assistantMessageEvent.delta), isThinking: true } };
|
|
274
|
+
}
|
|
275
|
+
else if (update.assistantMessageEvent?.type === "error" && update.assistantMessageEvent?.error?.errorMessage) {
|
|
276
|
+
console.warn("[agent.chat] model error:", update.assistantMessageEvent.error.errorMessage);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
else if (event.type === "tool_execution_start") {
|
|
280
|
+
wsPayload = { type: "chat", code: "agent.tool", payload: { type: "start", toolCallId: event.toolCallId, toolName: event.toolName, args: event.args } };
|
|
281
|
+
}
|
|
282
|
+
else if (event.type === "tool_execution_end") {
|
|
283
|
+
wsPayload = { type: "chat", code: "agent.tool", payload: { type: "end", toolCallId: event.toolCallId, toolName: event.toolName, result: event.result, isError: event.isError } };
|
|
284
|
+
}
|
|
285
|
+
else if (event.type === "message_end") {
|
|
286
|
+
const msg = event.message;
|
|
287
|
+
if (msg?.role === "assistant" && msg?.content && Array.isArray(msg.content)) {
|
|
288
|
+
const text = msg.content
|
|
289
|
+
.filter((c) => c?.type === "text" && typeof c.text === "string")
|
|
290
|
+
.map((c) => c.text)
|
|
291
|
+
.join("");
|
|
292
|
+
if (text && !hasReceivedAnyChunk) {
|
|
293
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "agent.chunk", payload: { text } });
|
|
294
|
+
}
|
|
295
|
+
hasReceivedAnyChunk = true;
|
|
296
|
+
}
|
|
297
|
+
if (msg?.errorMessage) {
|
|
298
|
+
// 调试:定位本地 LLM 流式报错来源(pi-ai 等 SDK 抛出的原始 errorMessage)
|
|
299
|
+
console.error("[agent.chat] message_end errorMessage:", msg.errorMessage);
|
|
300
|
+
if (typeof msg.errorStack === "string")
|
|
301
|
+
console.error("[agent.chat] message_end errorStack:", msg.errorStack);
|
|
302
|
+
let errText = msg.errorMessage.includes("402") || msg.errorMessage.includes("Insufficient Balance")
|
|
303
|
+
? "API 余额不足,请到「设置」检查并充值后重试。"
|
|
304
|
+
: `请求失败:${normalizeChunkText(msg.errorMessage)}`;
|
|
305
|
+
if (errText.includes("Unknown value type") && errText.includes("[object Object]")) {
|
|
306
|
+
errText = "请求失败:模型返回了不支持的数据结构(如工具调用流),请尝试关闭工具或更换模型。";
|
|
307
|
+
}
|
|
308
|
+
const isConnErr = /Connection error|ECONNREFUSED|fetch failed/i.test(msg.errorMessage);
|
|
309
|
+
const localFailed = process.env.LOCAL_LLM_START_FAILED;
|
|
310
|
+
const isLocalProvider = provider === "local";
|
|
311
|
+
const isModelRequired = /model is required|400.*model/i.test(msg.errorMessage);
|
|
312
|
+
if (isLocalProvider && localFailed && (msg.errorMessage === "terminated" || isConnErr)) {
|
|
313
|
+
errText = `请求失败:${localFailed}`;
|
|
314
|
+
}
|
|
315
|
+
else if ((provider === "openai-custom" || provider === "ollama") && isConnErr) {
|
|
316
|
+
errText = "请求失败:无法连接到模型服务(若使用 Ollama 请确认已启动且 baseUrl 为 http://localhost:11434/v1,或改用「Ollama」Provider)。";
|
|
317
|
+
}
|
|
318
|
+
else if (isModelRequired && (provider === "openai-custom" || provider === "ollama")) {
|
|
319
|
+
errText =
|
|
320
|
+
"请求失败:模型名称未被服务端识别。若使用 Ollama,请确保「模型配置」中的模型名与终端中 `ollama list` 显示的名称完全一致(如 qwen3:4b)。";
|
|
321
|
+
}
|
|
322
|
+
else if (provider === "local" && /context size.*too large|VRAM|显存/i.test(msg.errorMessage)) {
|
|
323
|
+
errText =
|
|
324
|
+
"请求失败:显存/内存不足,当前上下文长度过大。请在「智能体配置」中将该智能体的「上下文长度」调小(如 8192 或 4096)后重新启动本地模型服务再试。";
|
|
325
|
+
}
|
|
326
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "agent.chunk", payload: { text: errText } });
|
|
327
|
+
}
|
|
328
|
+
wsPayload = null;
|
|
329
|
+
}
|
|
330
|
+
else if (event.type === "turn_end") {
|
|
331
|
+
const msg = event.message;
|
|
332
|
+
if (!hasReceivedAnyChunk && msg?.content && Array.isArray(msg.content)) {
|
|
333
|
+
const fullText = msg.content
|
|
334
|
+
.filter((c) => c?.type === "text" && typeof c.text === "string")
|
|
335
|
+
.map((c) => c.text)
|
|
336
|
+
.join("");
|
|
337
|
+
if (fullText) {
|
|
338
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "agent.chunk", payload: { text: fullText } });
|
|
339
|
+
hasReceivedAnyChunk = true;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (msg?.errorMessage) {
|
|
343
|
+
// 调试:定位 turn_end 时 SDK 传入的原始错误
|
|
344
|
+
console.error("[agent.chat] turn_end errorMessage:", msg.errorMessage);
|
|
345
|
+
if (typeof msg.errorStack === "string")
|
|
346
|
+
console.error("[agent.chat] turn_end errorStack:", msg.errorStack);
|
|
347
|
+
let errText = msg.errorMessage.includes("402") || msg.errorMessage.includes("Insufficient Balance")
|
|
348
|
+
? "API 余额不足,请到「设置」检查并充值后重试。"
|
|
349
|
+
: `请求失败:${normalizeChunkText(msg.errorMessage)}`;
|
|
350
|
+
if (errText.includes("Unknown value type") && errText.includes("[object Object]")) {
|
|
351
|
+
errText = "请求失败:模型返回了不支持的数据结构(如工具调用流),请尝试关闭工具或更换模型。";
|
|
352
|
+
}
|
|
353
|
+
const isConnErr = /Connection error|ECONNREFUSED|fetch failed/i.test(msg.errorMessage);
|
|
354
|
+
const localFailed = process.env.LOCAL_LLM_START_FAILED;
|
|
355
|
+
const isLocalProvider = provider === "local";
|
|
356
|
+
const isModelRequired = /model is required|400.*model/i.test(msg.errorMessage);
|
|
357
|
+
if (isLocalProvider && localFailed && (msg.errorMessage === "terminated" || isConnErr)) {
|
|
358
|
+
errText = `请求失败:${localFailed}`;
|
|
359
|
+
}
|
|
360
|
+
else if ((provider === "openai-custom" || provider === "ollama") && isConnErr) {
|
|
361
|
+
errText = "请求失败:无法连接到模型服务(若使用 Ollama 请确认已启动且 baseUrl 为 http://localhost:11434/v1,或改用「Ollama」Provider)。";
|
|
362
|
+
}
|
|
363
|
+
else if (isModelRequired && (provider === "openai-custom" || provider === "ollama")) {
|
|
364
|
+
errText =
|
|
365
|
+
"请求失败:模型名称未被服务端识别。若使用 Ollama,请确保「模型配置」中的模型名与终端中 `ollama list` 显示的名称完全一致(如 qwen3:4b)。";
|
|
366
|
+
}
|
|
367
|
+
else if (provider === "local" && /context size.*too large|VRAM|显存/i.test(msg.errorMessage)) {
|
|
368
|
+
errText =
|
|
369
|
+
"请求失败:显存/内存不足,当前上下文长度过大。请在「智能体配置」中将该智能体的「上下文长度」调小(如 8192 或 4096)后重新启动本地模型服务再试。";
|
|
370
|
+
}
|
|
371
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "agent.chunk", payload: { text: errText } });
|
|
372
|
+
hasReceivedAnyChunk = true;
|
|
373
|
+
}
|
|
374
|
+
const usage = msg?.usage;
|
|
375
|
+
const promptTokens = Number(usage?.input ?? usage?.input_tokens ?? 0) || 0;
|
|
376
|
+
const completionTokens = Number(usage?.output ?? usage?.output_tokens ?? 0) || 0;
|
|
377
|
+
const usagePayload = promptTokens > 0 || completionTokens > 0 ? { promptTokens, completionTokens } : undefined;
|
|
378
|
+
const turnPayload = { content: "", ...(usagePayload && { usage: usagePayload }) };
|
|
379
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "turn_end", payload: turnPayload });
|
|
380
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "message_complete", payload: turnPayload });
|
|
381
|
+
wsPayload = null;
|
|
382
|
+
}
|
|
383
|
+
else if (event.type === "agent_end") {
|
|
384
|
+
if (!hasReceivedAnyChunk && Array.isArray(event.messages)) {
|
|
385
|
+
const messages = event.messages;
|
|
386
|
+
const lastAssistant = [...messages].reverse().find((m) => m?.role === "assistant" && m?.content);
|
|
387
|
+
if (lastAssistant?.content) {
|
|
388
|
+
const text = lastAssistant.content
|
|
389
|
+
.filter((c) => c?.type === "text" && typeof c.text === "string")
|
|
390
|
+
.map((c) => c.text)
|
|
391
|
+
.join("");
|
|
392
|
+
if (text)
|
|
393
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "agent.chunk", payload: { text } });
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "agent_end", payload: {} });
|
|
397
|
+
sendSessionMessage(targetSessionId, { type: "chat", code: "conversation_end", payload: {} });
|
|
398
|
+
wsPayload = null;
|
|
399
|
+
resolveAgentDone();
|
|
400
|
+
doUnsubscribe();
|
|
401
|
+
if (isEphemeralSession) {
|
|
402
|
+
void agentManager.deleteSession(targetSessionId + COMPOSITE_KEY_SEP + currentAgentId).catch(() => { });
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if (wsPayload) {
|
|
406
|
+
sendSessionMessage(targetSessionId, wsPayload);
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
sessionSubscriptionBySessionId.set(targetSessionId, unsubscribe);
|
|
410
|
+
try {
|
|
411
|
+
await session.sendUserMessage(message, { deliverAs: "followUp" });
|
|
412
|
+
// 流已启动,立即返回;前端以 agent_end 判断整轮结束,超时以「首包」计算更优
|
|
413
|
+
return { status: "streaming", sessionId: targetSessionId };
|
|
414
|
+
}
|
|
415
|
+
catch (error) {
|
|
416
|
+
console.error(`Error in agent chat:`, error);
|
|
417
|
+
resolveAgentDone();
|
|
418
|
+
doUnsubscribe();
|
|
419
|
+
throw error;
|
|
420
|
+
}
|
|
162
421
|
}
|
|
163
422
|
finally {
|
|
164
|
-
|
|
423
|
+
unregisterConsumer();
|
|
165
424
|
}
|
|
166
425
|
}
|
|
@@ -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,
|
|
@@ -58,7 +61,10 @@ export async function handleRunScheduledTask(req, res) {
|
|
|
58
61
|
modelId,
|
|
59
62
|
apiKey,
|
|
60
63
|
mcpServers: agentConfig?.mcpServers,
|
|
64
|
+
mcpMaxResultTokens: agentConfig?.mcpMaxResultTokens,
|
|
61
65
|
systemPrompt: agentConfig?.systemPrompt,
|
|
66
|
+
useLongMemory: agentConfig?.useLongMemory,
|
|
67
|
+
webSearch: agentConfig?.webSearch,
|
|
62
68
|
});
|
|
63
69
|
let assistantContent = "";
|
|
64
70
|
let turnPromptTokens = 0;
|
|
@@ -78,10 +84,6 @@ export async function handleRunScheduledTask(req, res) {
|
|
|
78
84
|
}
|
|
79
85
|
}
|
|
80
86
|
});
|
|
81
|
-
const experienceBlock = await getExperienceContextForUserMessage();
|
|
82
|
-
const userMessageToSend = experienceBlock.trim().length > 0
|
|
83
|
-
? `${experienceBlock}\n\n用户问题:\n${message}`
|
|
84
|
-
: message;
|
|
85
87
|
// 定时任务复用同一 session:若上次执行未结束会报 "Agent is already processing"。先等待空闲再发,避免并发。
|
|
86
88
|
const idleTimeoutMs = 10 * 60 * 1000;
|
|
87
89
|
const pollMs = 2000;
|
|
@@ -93,7 +95,7 @@ export async function handleRunScheduledTask(req, res) {
|
|
|
93
95
|
if (session.isStreaming) {
|
|
94
96
|
throw new Error("Session still busy after waiting; try again later.");
|
|
95
97
|
}
|
|
96
|
-
await session.sendUserMessage(
|
|
98
|
+
await session.sendUserMessage(message);
|
|
97
99
|
unsubscribe();
|
|
98
100
|
if (backendBaseUrl && assistantContent !== undefined) {
|
|
99
101
|
const url = `${backendBaseUrl.replace(/\/$/, "")}/server-api/agents/sessions/${encodeURIComponent(sessionId)}/messages`;
|
|
@@ -124,6 +126,6 @@ export async function handleRunScheduledTask(req, res) {
|
|
|
124
126
|
res.end(JSON.stringify({ success: false, error: friendlyError }));
|
|
125
127
|
}
|
|
126
128
|
finally {
|
|
127
|
-
agentManager.deleteSession(sessionId + COMPOSITE_KEY_SEP + sessionAgentId);
|
|
129
|
+
await agentManager.deleteSession(sessionId + COMPOSITE_KEY_SEP + sessionAgentId);
|
|
128
130
|
}
|
|
129
131
|
}
|
|
@@ -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
|
+
}
|