@next-open-ai/openclawx 0.8.28 → 0.8.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -45
- package/apps/desktop/renderer/dist/assets/{index-D9748VrD.js → index-BGHtXhm3.js} +30 -30
- package/apps/desktop/renderer/dist/assets/index-CB2-m4ae.css +10 -0
- package/apps/desktop/renderer/dist/index.html +2 -2
- package/dist/core/agent/agent-manager.js +5 -2
- package/dist/core/agent/proxy/adapters/opencode-adapter.js +48 -14
- 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/index.d.ts +3 -1
- package/dist/core/mcp/index.js +4 -1
- 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/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/gateway/channel/channel-core.d.ts +1 -0
- package/dist/gateway/channel/channel-core.js +101 -59
- package/dist/gateway/methods/agent-cancel.js +3 -0
- package/dist/gateway/methods/agent-chat.d.ts +4 -0
- package/dist/gateway/methods/agent-chat.js +293 -229
- package/dist/gateway/server.js +2 -0
- package/package.json +1 -1
- package/apps/desktop/renderer/dist/assets/index-DVbrfeA6.css +0 -10
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { runAgentAndCollectReply, runAgentAndStreamReply } from "./run-agent.js";
|
|
2
2
|
import { getChannelSessionPersistence, persistChannelUserMessage, persistChannelAssistantMessage } from "./session-persistence.js";
|
|
3
|
+
import { preprocessInboundMessage } from "../../core/inbound-message-preprocess.js";
|
|
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";
|
|
3
9
|
const STREAM_THROTTLE_MS = 280;
|
|
4
10
|
function toSessionId(channelId, threadId) {
|
|
5
11
|
return `channel:${channelId}:${threadId}`;
|
|
@@ -61,75 +67,111 @@ export async function handleChannelMessage(channel, msg) {
|
|
|
61
67
|
console.warn("[ChannelCore] invalid threadId, skip reply");
|
|
62
68
|
return;
|
|
63
69
|
}
|
|
64
|
-
|
|
65
|
-
await
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
const rawMessage = msg.messageText?.trim() ?? "";
|
|
71
|
+
const { message: messageText, agentId: effectiveAgentId, directResponse } = await preprocessInboundMessage({
|
|
72
|
+
sessionId,
|
|
73
|
+
message: rawMessage,
|
|
74
|
+
currentAgentId,
|
|
69
75
|
});
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
76
|
+
getSessionCurrentAgentUpdater()?.(sessionId, effectiveAgentId);
|
|
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;
|
|
75
101
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
102
|
+
// 与 Web/Desktop 统一:通道会话入库并追加用户消息(用预处理后的 agentId 与消息)
|
|
103
|
+
await persistChannelUserMessage(sessionId, {
|
|
104
|
+
agentId: effectiveAgentId,
|
|
105
|
+
title: messageText.slice(0, 50) || undefined,
|
|
106
|
+
messageText,
|
|
107
|
+
});
|
|
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
|
+
}
|
|
80
155
|
return;
|
|
81
156
|
}
|
|
82
|
-
let
|
|
83
|
-
let donePromise = null;
|
|
84
|
-
const throttled = throttle(() => {
|
|
85
|
-
if (sink.onChunk)
|
|
86
|
-
void Promise.resolve(sink.onChunk(accumulated)).catch((e) => console.error("[ChannelCore] stream onChunk error:", e));
|
|
87
|
-
}, STREAM_THROTTLE_MS);
|
|
157
|
+
let replyText;
|
|
88
158
|
try {
|
|
89
|
-
await
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
},
|
|
94
|
-
onTurnEnd() {
|
|
95
|
-
throttled.flush();
|
|
96
|
-
if (sink.onTurnEnd)
|
|
97
|
-
void Promise.resolve(sink.onTurnEnd(accumulated)).catch((e) => console.error("[ChannelCore] stream onTurnEnd error:", e));
|
|
98
|
-
},
|
|
99
|
-
onDone() {
|
|
100
|
-
throttled.cancel();
|
|
101
|
-
const final = accumulated.trim() || "(无文本回复)";
|
|
102
|
-
persistChannelAssistantMessage(sessionId, final);
|
|
103
|
-
donePromise = Promise.resolve(sink.onDone(final)).catch((e) => console.error("[ChannelCore] stream onDone error:", e));
|
|
104
|
-
},
|
|
159
|
+
replyText = await runAgentAndCollectReply({
|
|
160
|
+
sessionId,
|
|
161
|
+
message: messageText,
|
|
162
|
+
agentId: effectiveAgentId,
|
|
105
163
|
});
|
|
106
|
-
if (donePromise)
|
|
107
|
-
await donePromise;
|
|
108
|
-
await msg.ack?.(undefined);
|
|
109
164
|
}
|
|
110
165
|
catch (err) {
|
|
111
166
|
console.error("[ChannelCore] runAgent failed:", err);
|
|
112
|
-
|
|
113
|
-
const fallback = accumulated.trim() || "处理时出错,请稍后再试。";
|
|
114
|
-
await Promise.resolve(sink.onDone(fallback)).catch(() => { });
|
|
115
|
-
await msg.ack?.(undefined);
|
|
167
|
+
replyText = "处理时出错,请稍后再试。";
|
|
116
168
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
replyText = await runAgentAndCollectReply({
|
|
122
|
-
sessionId,
|
|
123
|
-
message: msg.messageText,
|
|
124
|
-
agentId: currentAgentId,
|
|
125
|
-
});
|
|
169
|
+
persistChannelAssistantMessage(sessionId, replyText);
|
|
170
|
+
const reply = { text: replyText };
|
|
171
|
+
const sendResult = await outbound.send(threadId, reply);
|
|
172
|
+
await msg.ack?.(sendResult);
|
|
126
173
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
replyText = "处理时出错,请稍后再试。";
|
|
174
|
+
finally {
|
|
175
|
+
unregisterConsumer();
|
|
130
176
|
}
|
|
131
|
-
persistChannelAssistantMessage(sessionId, replyText);
|
|
132
|
-
const reply = { text: replyText };
|
|
133
|
-
const sendResult = await outbound.send(threadId, reply);
|
|
134
|
-
await msg.ack?.(sendResult);
|
|
135
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
|
*/
|