@ynhcj/xiaoyi-channel 1.1.26 → 1.1.28
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/dist/index.js +26 -69
- package/dist/src/approval-bridge.d.ts +48 -0
- package/dist/src/approval-bridge.js +382 -0
- package/dist/src/bot.js +132 -73
- package/dist/src/channel.js +59 -5
- package/dist/src/client.js +13 -23
- package/dist/src/cron-command.d.ts +15 -0
- package/dist/src/cron-command.js +49 -0
- package/dist/src/cron-query-handler.d.ts +7 -0
- package/dist/src/cron-query-handler.js +189 -0
- package/dist/src/cspl/call_api.d.ts +2 -0
- package/dist/src/cspl/call_api.js +107 -0
- package/dist/src/cspl/config.d.ts +4 -17
- package/dist/src/cspl/config.js +100 -70
- package/dist/src/cspl/configs.json +10 -0
- package/dist/src/cspl/constants.d.ts +49 -24
- package/dist/src/cspl/constants.js +46 -16
- package/dist/src/cspl/sentinel_hook.d.ts +2 -0
- package/dist/src/cspl/sentinel_hook.js +103 -0
- package/dist/src/cspl/steer-context.js +1 -1
- package/dist/src/cspl/upload_file.d.ts +1 -0
- package/dist/src/cspl/upload_file.js +211 -0
- package/dist/src/cspl/utils.d.ts +17 -2
- package/dist/src/cspl/utils.js +271 -15
- package/dist/src/file-upload.d.ts +5 -0
- package/dist/src/file-upload.js +102 -0
- package/dist/src/formatter.d.ts +43 -1
- package/dist/src/formatter.js +171 -41
- package/dist/src/monitor.js +64 -43
- package/dist/src/outbound.js +8 -9
- package/dist/src/parser.d.ts +8 -1
- package/dist/src/parser.js +71 -0
- package/dist/src/provider.js +51 -17
- package/dist/src/push.d.ts +11 -1
- package/dist/src/push.js +101 -17
- package/dist/src/reply-dispatcher.js +152 -59
- package/dist/src/self-evolution-handler.d.ts +1 -1
- package/dist/src/self-evolution-handler.js +14 -3
- package/dist/src/sensitive-redactor.d.ts +4 -0
- package/dist/src/sensitive-redactor.js +364 -0
- package/dist/src/task-manager.js +6 -10
- package/dist/src/tools/agent-as-skill-tool.d.ts +7 -0
- package/dist/src/tools/agent-as-skill-tool.js +190 -0
- package/dist/src/tools/calendar-tool.js +3 -2
- package/dist/src/tools/call-phone-tool.js +3 -2
- package/dist/src/tools/check-plugin-privilege-tool.d.ts +6 -0
- package/dist/src/tools/check-plugin-privilege-tool.js +182 -0
- package/dist/src/tools/create-alarm-tool.js +3 -2
- package/dist/src/tools/create-all-tools.js +11 -3
- package/dist/src/tools/delete-alarm-tool.js +3 -2
- package/dist/src/tools/device-tool-map.d.ts +1 -1
- package/dist/src/tools/device-tool-map.js +12 -5
- package/dist/src/tools/discover-cross-devices-tool.d.ts +2 -0
- package/dist/src/tools/discover-cross-devices-tool.js +235 -0
- package/dist/src/tools/display-a2ui-card-tool.d.ts +2 -0
- package/dist/src/tools/display-a2ui-card-tool.js +85 -0
- package/dist/src/tools/find-pc-devices-tool.d.ts +2 -1
- package/dist/src/tools/find-pc-devices-tool.js +85 -88
- package/dist/src/tools/get-collection-tool-schema.js +1 -1
- package/dist/src/tools/location-tool.js +3 -2
- package/dist/src/tools/modify-alarm-tool.js +3 -2
- package/dist/src/tools/modify-note-tool.js +3 -2
- package/dist/src/tools/note-tool.js +3 -2
- package/dist/src/tools/query-app-message-tool.js +4 -3
- package/dist/src/tools/query-memory-data-tool.js +4 -3
- package/dist/src/tools/query-todo-task-tool.js +4 -3
- package/dist/src/tools/save-file-to-phone-tool.js +3 -2
- package/dist/src/tools/save-media-to-gallery-tool.js +3 -2
- package/dist/src/tools/schema-tool-factory.js +1 -1
- package/dist/src/tools/search-alarm-tool.js +3 -2
- package/dist/src/tools/search-calendar-tool.js +3 -2
- package/dist/src/tools/search-contact-tool.js +3 -2
- package/dist/src/tools/search-email-tool.js +4 -3
- package/dist/src/tools/search-file-tool.js +8 -9
- package/dist/src/tools/search-message-tool.js +2 -1
- package/dist/src/tools/search-note-tool.js +3 -2
- package/dist/src/tools/search-photo-gallery-tool.js +5 -4
- package/dist/src/tools/send-cross-device-task-tool.d.ts +2 -0
- package/dist/src/tools/send-cross-device-task-tool.js +299 -0
- package/dist/src/tools/send-email-tool.js +4 -3
- package/dist/src/tools/send-file-to-user-tool.d.ts +1 -1
- package/dist/src/tools/send-file-to-user-tool.js +37 -8
- package/dist/src/tools/send-html-card-tool.d.ts +7 -0
- package/dist/src/tools/send-html-card-tool.js +113 -0
- package/dist/src/tools/send-message-tool.js +2 -1
- package/dist/src/tools/session-manager.d.ts +17 -1
- package/dist/src/tools/session-manager.js +87 -1
- package/dist/src/tools/upload-file-tool.js +9 -7
- package/dist/src/tools/upload-photo-tool.js +5 -4
- package/dist/src/tools/xiaoyi-add-collection-tool.js +5 -3
- package/dist/src/tools/xiaoyi-collection-tool.js +4 -3
- package/dist/src/tools/xiaoyi-delete-collection-tool.js +4 -3
- package/dist/src/tools/xiaoyi-gui-tool.js +8 -2
- package/dist/src/trigger-handler.js +4 -7
- package/dist/src/types.d.ts +25 -1
- package/dist/src/utils/config-manager.js +3 -6
- package/dist/src/utils/logger.d.ts +8 -0
- package/dist/src/utils/logger.js +69 -34
- package/dist/src/utils/pushdata-manager.js +1 -5
- package/dist/src/utils/pushid-manager.js +1 -2
- package/dist/src/utils/runtime-manager.js +1 -4
- package/dist/src/websocket.d.ts +3 -0
- package/dist/src/websocket.js +242 -38
- package/package.json +1 -1
|
@@ -1,11 +1,56 @@
|
|
|
1
1
|
import { getXYRuntime } from "./runtime.js";
|
|
2
|
-
import { sendA2AResponse, sendStatusUpdate, sendReasoningTextUpdate } from "./formatter.js";
|
|
2
|
+
import { sendA2AResponse, sendStatusUpdate, sendReasoningTextUpdate, sendCommand } from "./formatter.js";
|
|
3
3
|
import { resolveXYConfig } from "./config.js";
|
|
4
4
|
import { getCurrentTaskId, getCurrentMessageId } from "./task-manager.js";
|
|
5
|
+
import { clearRunCrossTaskSentFiles, getCurrentSessionContext } from "./tools/session-manager.js";
|
|
5
6
|
import fs from "fs/promises";
|
|
6
7
|
import path from "path";
|
|
7
8
|
import { logger } from "./utils/logger.js";
|
|
8
9
|
const TEMP_FILE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
10
|
+
const RUN_CROSS_TASK_LOG_TAG = "[RunCrossTask]";
|
|
11
|
+
function buildDistributionStatusCommand(context) {
|
|
12
|
+
return {
|
|
13
|
+
header: {
|
|
14
|
+
namespace: "DistributionInteraction",
|
|
15
|
+
name: "DistributionStatus",
|
|
16
|
+
},
|
|
17
|
+
payload: {
|
|
18
|
+
agentId: context.agentId,
|
|
19
|
+
isDistributed: true,
|
|
20
|
+
networkId: context.networkId,
|
|
21
|
+
distributionType: "softbus",
|
|
22
|
+
distributionExecutePolicy: "backgroundExecution",
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function buildCrossTaskExecuteResultCommand(code, message, sentFiles = []) {
|
|
27
|
+
return {
|
|
28
|
+
header: {
|
|
29
|
+
namespace: "DistributionInteraction",
|
|
30
|
+
name: "CrossTaskExecuteResult",
|
|
31
|
+
},
|
|
32
|
+
payload: {
|
|
33
|
+
code,
|
|
34
|
+
message,
|
|
35
|
+
sentFiles,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
async function sendRunCrossTaskResult(params) {
|
|
40
|
+
const { config, sessionId, taskId, messageId, context, resultCode, resultMessage } = params;
|
|
41
|
+
const sentFiles = Array.isArray(context.sentFiles) ? context.sentFiles : [];
|
|
42
|
+
const statusCommand = buildDistributionStatusCommand(context);
|
|
43
|
+
const resultCommand = buildCrossTaskExecuteResultCommand(resultCode, resultMessage, sentFiles);
|
|
44
|
+
await sendCommand({
|
|
45
|
+
config,
|
|
46
|
+
sessionId,
|
|
47
|
+
taskId,
|
|
48
|
+
messageId,
|
|
49
|
+
commands: [statusCommand, resultCommand],
|
|
50
|
+
});
|
|
51
|
+
clearRunCrossTaskSentFiles(context);
|
|
52
|
+
logger.log(`${RUN_CROSS_TASK_LOG_TAG} sent cross-task result, sessionId=${sessionId}, taskId=${taskId}, code=${resultCode}, sentFileCount=${sentFiles.length}, clearedSentFileCount=${sentFiles.length}, messageLength=${resultMessage.length}`);
|
|
53
|
+
}
|
|
9
54
|
/**
|
|
10
55
|
* 清理 /tmp/xy_channel 目录中超过 24 小时的旧文件
|
|
11
56
|
*/
|
|
@@ -32,11 +77,11 @@ export async function cleanupStaleTempFiles(tempDir = "/tmp/xy_channel") {
|
|
|
32
77
|
}
|
|
33
78
|
}
|
|
34
79
|
if (cleanedCount > 0) {
|
|
35
|
-
logger.log(`[CLEANUP]
|
|
80
|
+
logger.log(`[CLEANUP] Cleaned ${cleanedCount} stale files (>${TEMP_FILE_TTL_MS / 1000 / 3600}h) from ${tempDir}`);
|
|
36
81
|
}
|
|
37
82
|
}
|
|
38
83
|
catch (err) {
|
|
39
|
-
logger.error(`[CLEANUP]
|
|
84
|
+
logger.error(`[CLEANUP] Failed to cleanup temp dir:`, err);
|
|
40
85
|
}
|
|
41
86
|
}
|
|
42
87
|
/**
|
|
@@ -46,21 +91,19 @@ export async function cleanupStaleTempFiles(tempDir = "/tmp/xy_channel") {
|
|
|
46
91
|
*/
|
|
47
92
|
export function createXYReplyDispatcher(params) {
|
|
48
93
|
const { cfg, runtime, sessionId, taskId, messageId, accountId, steerState } = params;
|
|
49
|
-
logger.log(`[DISPATCHER-CREATE] ******* Creating dispatcher *******`);
|
|
50
|
-
logger.log(`[DISPATCHER-CREATE] - taskId: ${taskId}`);
|
|
51
94
|
// 初始taskId和messageId(作为fallback)
|
|
52
95
|
const initialTaskId = taskId;
|
|
53
96
|
const initialMessageId = messageId;
|
|
54
|
-
/**
|
|
55
|
-
* 🔑 核心改造:动态获取当前活跃的taskId和messageId
|
|
56
|
-
* 每次需要taskId时,都从TaskManager获取最新值
|
|
57
|
-
*/
|
|
58
97
|
const getActiveTaskId = () => {
|
|
59
98
|
return getCurrentTaskId(sessionId) ?? initialTaskId;
|
|
60
99
|
};
|
|
61
100
|
const getActiveMessageId = () => {
|
|
62
101
|
return getCurrentMessageId(sessionId) ?? initialMessageId;
|
|
63
102
|
};
|
|
103
|
+
// Create a scoped logger that always uses this session's sessionId
|
|
104
|
+
// and dynamically resolves the latest taskId
|
|
105
|
+
const scopedLog = () => logger.withContext(sessionId, getActiveTaskId());
|
|
106
|
+
scopedLog().log(`[DISPATCHER-CREATE] Creating dispatcher`);
|
|
64
107
|
const core = getXYRuntime();
|
|
65
108
|
const config = resolveXYConfig(cfg);
|
|
66
109
|
// Simplified prefix context for single-account Xiaoyi channel
|
|
@@ -73,18 +116,20 @@ export function createXYReplyDispatcher(params) {
|
|
|
73
116
|
let hasSentResponse = false;
|
|
74
117
|
let finalSent = false;
|
|
75
118
|
let accumulatedText = "";
|
|
119
|
+
const initialRunCrossTaskContext = getCurrentSessionContext()?.runCrossTaskContext;
|
|
120
|
+
const getRunCrossTaskContext = () => {
|
|
121
|
+
return getCurrentSessionContext()?.runCrossTaskContext ?? initialRunCrossTaskContext;
|
|
122
|
+
};
|
|
76
123
|
/**
|
|
77
124
|
* Start the status update interval
|
|
78
125
|
*/
|
|
79
126
|
const startStatusInterval = () => {
|
|
80
|
-
|
|
127
|
+
scopedLog().log(`[STATUS-INTERVAL] Starting interval`);
|
|
81
128
|
statusUpdateInterval = setInterval(() => {
|
|
82
129
|
// 🔑 使用动态taskId
|
|
83
130
|
const currentTaskId = getActiveTaskId();
|
|
84
131
|
const currentMessageId = getActiveMessageId();
|
|
85
|
-
|
|
86
|
-
logger.log(`[STATUS INTERVAL] - sessionId: ${sessionId}`);
|
|
87
|
-
logger.log(`[STATUS INTERVAL] - currentTaskId: ${currentTaskId}`);
|
|
132
|
+
scopedLog().log(`[STATUS-INTERVAL] Triggering status update, taskId=${currentTaskId}`);
|
|
88
133
|
void sendStatusUpdate({
|
|
89
134
|
config,
|
|
90
135
|
sessionId,
|
|
@@ -93,13 +138,13 @@ export function createXYReplyDispatcher(params) {
|
|
|
93
138
|
text: "任务正在处理中,请稍候~",
|
|
94
139
|
state: "working",
|
|
95
140
|
}).catch((err) => {
|
|
96
|
-
|
|
141
|
+
scopedLog().error(`Failed to send status update:`, err);
|
|
97
142
|
});
|
|
98
143
|
}, 30000); // 30 seconds
|
|
99
144
|
};
|
|
100
145
|
const stopStatusInterval = () => {
|
|
101
146
|
if (statusUpdateInterval) {
|
|
102
|
-
|
|
147
|
+
scopedLog().log(`[STATUS-INTERVAL] Stopping interval`);
|
|
103
148
|
clearInterval(statusUpdateInterval);
|
|
104
149
|
statusUpdateInterval = null;
|
|
105
150
|
}
|
|
@@ -110,38 +155,43 @@ export function createXYReplyDispatcher(params) {
|
|
|
110
155
|
humanDelay: core.channel.reply.resolveHumanDelayConfig(cfg, accountId),
|
|
111
156
|
onReplyStart: () => {
|
|
112
157
|
const currentTaskId = getActiveTaskId();
|
|
113
|
-
|
|
158
|
+
scopedLog().log(`[REPLY-START] Reply started, taskId=${currentTaskId}, steered=${steerState.steered}`);
|
|
114
159
|
},
|
|
115
160
|
deliver: async (payload, info) => {
|
|
116
161
|
// 🔑 steered dispatch不发送内容(让主dispatcher处理)
|
|
117
162
|
if (steerState.steered) {
|
|
118
|
-
|
|
163
|
+
scopedLog().log(`[DELIVER] Steered dispatch, skipping, kind=${info?.kind}`);
|
|
119
164
|
return;
|
|
120
165
|
}
|
|
121
166
|
const text = payload.text ?? "";
|
|
122
167
|
const currentTaskId = getActiveTaskId();
|
|
123
168
|
const currentMessageId = getActiveMessageId();
|
|
124
|
-
|
|
169
|
+
scopedLog().log(`[DELIVER] kind=${info?.kind}, text.length=${text.length}`);
|
|
125
170
|
try {
|
|
126
171
|
if (!text.trim()) {
|
|
127
|
-
|
|
172
|
+
scopedLog().log(`[DELIVER SKIP] Empty text, skipping`);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
// 🔑 如果 onPartialReply 已经流式发送过文本,deliver 不再重复发送
|
|
176
|
+
if (hasSentResponse) {
|
|
177
|
+
scopedLog().log(`[DELIVER SKIP] Already sent via onPartialReply`);
|
|
128
178
|
return;
|
|
129
179
|
}
|
|
130
180
|
accumulatedText += text;
|
|
131
181
|
hasSentResponse = true;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
await sendReasoningTextUpdate({
|
|
182
|
+
// 🔑 使用动态taskId发送A2A响应(流式append)
|
|
183
|
+
await sendA2AResponse({
|
|
135
184
|
config,
|
|
136
185
|
sessionId,
|
|
137
186
|
taskId: currentTaskId,
|
|
138
187
|
messageId: currentMessageId,
|
|
139
188
|
text,
|
|
189
|
+
append: true,
|
|
190
|
+
final: false,
|
|
140
191
|
});
|
|
141
|
-
logger.log(`[DELIVER] ✅ Sent deliver text as reasoningText update`);
|
|
142
192
|
}
|
|
143
193
|
catch (deliverError) {
|
|
144
|
-
|
|
194
|
+
scopedLog().error(`Failed to deliver message:`, deliverError);
|
|
145
195
|
}
|
|
146
196
|
},
|
|
147
197
|
onError: async (err, info) => {
|
|
@@ -149,7 +199,7 @@ export function createXYReplyDispatcher(params) {
|
|
|
149
199
|
stopStatusInterval();
|
|
150
200
|
// 🔑 steered dispatcher不发送错误状态(让主dispatcher处理)
|
|
151
201
|
if (steerState.steered) {
|
|
152
|
-
|
|
202
|
+
scopedLog().log(`[ON-ERROR] Steered dispatch, skipping error response`);
|
|
153
203
|
return;
|
|
154
204
|
}
|
|
155
205
|
if (!hasSentResponse) {
|
|
@@ -166,29 +216,36 @@ export function createXYReplyDispatcher(params) {
|
|
|
166
216
|
});
|
|
167
217
|
}
|
|
168
218
|
catch (statusError) {
|
|
169
|
-
|
|
219
|
+
scopedLog().error(`Failed to send error status:`, statusError);
|
|
170
220
|
}
|
|
171
221
|
}
|
|
172
222
|
},
|
|
173
223
|
onIdle: async () => {
|
|
174
224
|
const currentTaskId = getActiveTaskId();
|
|
175
225
|
const currentMessageId = getActiveMessageId();
|
|
176
|
-
|
|
177
|
-
logger.log(`[ON_IDLE] - sessionId: ${sessionId}`);
|
|
178
|
-
logger.log(`[ON_IDLE] - taskId: ${currentTaskId}`);
|
|
179
|
-
logger.log(`[ON_IDLE] - steered: ${steerState.steered}`);
|
|
180
|
-
logger.log(`[ON_IDLE] - hasSentResponse: ${hasSentResponse}`);
|
|
181
|
-
logger.log(`[ON_IDLE] - finalSent: ${finalSent}`);
|
|
226
|
+
scopedLog().log(`[ON-IDLE] Reply idle, steered=${steerState.steered}, hasSentResponse=${hasSentResponse}, finalSent=${finalSent}`);
|
|
182
227
|
// 🔑 steered dispatch不发送final响应(核心已注入到活跃 Pi run)
|
|
183
228
|
if (steerState.steered) {
|
|
184
|
-
|
|
229
|
+
scopedLog().log(`[ON-IDLE] Steered dispatch, skipping final response`);
|
|
185
230
|
stopStatusInterval();
|
|
186
231
|
return; // ← 直接返回,不发送任何东西!
|
|
187
232
|
}
|
|
188
233
|
// 正常模式(或未被steer的dispatch)
|
|
189
234
|
if (hasSentResponse && !finalSent) {
|
|
190
|
-
|
|
235
|
+
scopedLog().log(`[ON-IDLE] Sending accumulated text, length=${accumulatedText.length}`);
|
|
191
236
|
try {
|
|
237
|
+
const runCrossTaskContext = getRunCrossTaskContext();
|
|
238
|
+
if (runCrossTaskContext) {
|
|
239
|
+
await sendRunCrossTaskResult({
|
|
240
|
+
config,
|
|
241
|
+
sessionId,
|
|
242
|
+
taskId: currentTaskId,
|
|
243
|
+
messageId: currentMessageId,
|
|
244
|
+
context: runCrossTaskContext,
|
|
245
|
+
resultCode: "0",
|
|
246
|
+
resultMessage: accumulatedText,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
192
249
|
// 🔑 使用动态taskId发送完成状态
|
|
193
250
|
await sendStatusUpdate({
|
|
194
251
|
config,
|
|
@@ -198,28 +255,40 @@ export function createXYReplyDispatcher(params) {
|
|
|
198
255
|
text: "任务处理已完成~",
|
|
199
256
|
state: "completed",
|
|
200
257
|
});
|
|
201
|
-
|
|
202
|
-
// 🔑 使用动态taskId
|
|
258
|
+
scopedLog().log(`[ON-IDLE] Sent completion status update`);
|
|
259
|
+
// 🔑 使用动态taskId发送最终响应(空字符串表示流结束)
|
|
203
260
|
await sendA2AResponse({
|
|
204
261
|
config,
|
|
205
262
|
sessionId,
|
|
206
263
|
taskId: currentTaskId,
|
|
207
264
|
messageId: currentMessageId,
|
|
208
|
-
text:
|
|
209
|
-
append:
|
|
265
|
+
text: "",
|
|
266
|
+
append: true,
|
|
210
267
|
final: true,
|
|
211
268
|
});
|
|
212
269
|
finalSent = true;
|
|
213
|
-
|
|
270
|
+
scopedLog().log(`[ON-IDLE] Sent final response (empty, stream end)`);
|
|
214
271
|
}
|
|
215
272
|
catch (err) {
|
|
216
|
-
|
|
273
|
+
scopedLog().error(`[ON-IDLE] Failed to send final response:`, err);
|
|
217
274
|
}
|
|
218
275
|
}
|
|
219
276
|
else {
|
|
220
277
|
// 正常失败场景(非steered)
|
|
221
|
-
|
|
278
|
+
scopedLog().log(`[ON-IDLE] Skipping final message: hasSentResponse=${hasSentResponse}, finalSent=${finalSent}`);
|
|
222
279
|
try {
|
|
280
|
+
const runCrossTaskContext = getRunCrossTaskContext();
|
|
281
|
+
if (runCrossTaskContext) {
|
|
282
|
+
await sendRunCrossTaskResult({
|
|
283
|
+
config,
|
|
284
|
+
sessionId,
|
|
285
|
+
taskId: currentTaskId,
|
|
286
|
+
messageId: currentMessageId,
|
|
287
|
+
context: runCrossTaskContext,
|
|
288
|
+
resultCode: "1",
|
|
289
|
+
resultMessage: "任务执行异常,请重试",
|
|
290
|
+
});
|
|
291
|
+
}
|
|
223
292
|
await sendStatusUpdate({
|
|
224
293
|
config,
|
|
225
294
|
sessionId,
|
|
@@ -228,36 +297,37 @@ export function createXYReplyDispatcher(params) {
|
|
|
228
297
|
text: "任务处理中断了~",
|
|
229
298
|
state: "failed",
|
|
230
299
|
});
|
|
231
|
-
|
|
300
|
+
scopedLog().log(`[ON-IDLE] Sent failure status update`);
|
|
232
301
|
await sendA2AResponse({
|
|
233
302
|
config,
|
|
234
303
|
sessionId,
|
|
235
304
|
taskId: currentTaskId,
|
|
236
305
|
messageId: currentMessageId,
|
|
237
306
|
text: "任务执行异常,请重试~",
|
|
238
|
-
append:
|
|
307
|
+
append: true,
|
|
239
308
|
final: true,
|
|
240
309
|
errorCode: 99921111,
|
|
241
310
|
errorMessage: "任务执行异常,请重试",
|
|
242
311
|
});
|
|
243
312
|
finalSent = true;
|
|
244
|
-
|
|
313
|
+
scopedLog().log(`[ON-IDLE] Sent error response, code=99921111`);
|
|
245
314
|
}
|
|
246
315
|
catch (err) {
|
|
247
|
-
|
|
316
|
+
scopedLog().error(`[ON-IDLE] Failed to send error response:`, err);
|
|
248
317
|
}
|
|
249
318
|
}
|
|
250
319
|
stopStatusInterval();
|
|
251
320
|
},
|
|
252
321
|
onCleanup: () => {
|
|
253
322
|
const currentTaskId = getActiveTaskId();
|
|
254
|
-
|
|
323
|
+
scopedLog().log(`[ON-CLEANUP] Reply cleanup, steered=${steerState.steered}`);
|
|
255
324
|
},
|
|
256
325
|
});
|
|
257
326
|
return {
|
|
258
327
|
dispatcher,
|
|
259
328
|
replyOptions: {
|
|
260
329
|
...replyOptions,
|
|
330
|
+
suppressToolErrorWarnings: true,
|
|
261
331
|
onModelSelected: prefixContext.onModelSelected,
|
|
262
332
|
onToolStart: async ({ name, phase }) => {
|
|
263
333
|
// 🔑 steered dispatch不发送tool状态(让主dispatcher处理)
|
|
@@ -266,13 +336,13 @@ export function createXYReplyDispatcher(params) {
|
|
|
266
336
|
}
|
|
267
337
|
const currentTaskId = getActiveTaskId();
|
|
268
338
|
const currentMessageId = getActiveMessageId();
|
|
269
|
-
|
|
339
|
+
scopedLog().log(`[TOOL-START] Tool: ${name}, phase: ${phase}`);
|
|
270
340
|
if (phase === "start") {
|
|
271
341
|
const toolName = name || "unknown";
|
|
272
342
|
// call_device_tool 由自身 execute() 内部发送具体子工具名的状态更新
|
|
273
343
|
// get_xxx_tool_schema 是给 LLM 查 schema 用的,无需向用户展示
|
|
274
344
|
if (toolName === "call_device_tool" || toolName.endsWith("_tool_schema") || toolName === "huawei_id_tool") {
|
|
275
|
-
|
|
345
|
+
scopedLog().log(`[TOOL-START] Skipping generic status for ${toolName}`);
|
|
276
346
|
return;
|
|
277
347
|
}
|
|
278
348
|
try {
|
|
@@ -284,10 +354,10 @@ export function createXYReplyDispatcher(params) {
|
|
|
284
354
|
text: `正在使用工具: ${toolName}...`,
|
|
285
355
|
state: "working",
|
|
286
356
|
});
|
|
287
|
-
|
|
357
|
+
scopedLog().log(`[TOOL-START] Sent status update for tool start: ${toolName}`);
|
|
288
358
|
}
|
|
289
359
|
catch (err) {
|
|
290
|
-
|
|
360
|
+
scopedLog().error(`[TOOL-START] Failed to send tool start status:`, err);
|
|
291
361
|
}
|
|
292
362
|
}
|
|
293
363
|
},
|
|
@@ -300,7 +370,7 @@ export function createXYReplyDispatcher(params) {
|
|
|
300
370
|
const currentMessageId = getActiveMessageId();
|
|
301
371
|
const text = payload.text ?? "";
|
|
302
372
|
const hasMedia = Boolean(payload.mediaUrl || (payload.mediaUrls?.length ?? 0) > 0);
|
|
303
|
-
|
|
373
|
+
scopedLog().log(`[TOOL-RESULT] Tool result, text.length: ${text.length}`);
|
|
304
374
|
try {
|
|
305
375
|
if (text.length > 0 || hasMedia) {
|
|
306
376
|
const resultText = text.length > 0 ? text : "工具执行完成";
|
|
@@ -312,11 +382,11 @@ export function createXYReplyDispatcher(params) {
|
|
|
312
382
|
text: resultText,
|
|
313
383
|
state: "working",
|
|
314
384
|
});
|
|
315
|
-
|
|
385
|
+
scopedLog().log(`[TOOL-RESULT] Sent tool result as status update`);
|
|
316
386
|
}
|
|
317
387
|
}
|
|
318
388
|
catch (err) {
|
|
319
|
-
|
|
389
|
+
scopedLog().error(`[TOOL-RESULT] Failed to send tool result status:`, err);
|
|
320
390
|
}
|
|
321
391
|
},
|
|
322
392
|
onReasoningStream: async (payload) => {
|
|
@@ -324,10 +394,29 @@ export function createXYReplyDispatcher(params) {
|
|
|
324
394
|
if (steerState.steered) {
|
|
325
395
|
return;
|
|
326
396
|
}
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
//
|
|
397
|
+
const currentTaskId = getActiveTaskId();
|
|
398
|
+
const currentMessageId = getActiveMessageId();
|
|
399
|
+
let text = payload.text ?? "";
|
|
400
|
+
// Strip "Reasoning:" prefix that some reasoning models add to their thinking output
|
|
401
|
+
const lines = text.split(/\r?\n/);
|
|
402
|
+
if (lines[0]?.trim() === "Reasoning:") {
|
|
403
|
+
text = lines.slice(1).join("\n").trim();
|
|
404
|
+
}
|
|
405
|
+
try {
|
|
406
|
+
if (text.length > 0) {
|
|
407
|
+
await sendReasoningTextUpdate({
|
|
408
|
+
config,
|
|
409
|
+
sessionId,
|
|
410
|
+
taskId: currentTaskId,
|
|
411
|
+
messageId: currentMessageId,
|
|
412
|
+
text,
|
|
413
|
+
append: false,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
catch (err) {
|
|
418
|
+
scopedLog().error(`[REASONING-STREAM] Failed to send reasoning text:`, err);
|
|
419
|
+
}
|
|
331
420
|
},
|
|
332
421
|
onPartialReply: async (payload) => {
|
|
333
422
|
// 🔑 steered dispatch不发送partial reply(让主dispatcher处理)
|
|
@@ -339,18 +428,22 @@ export function createXYReplyDispatcher(params) {
|
|
|
339
428
|
const text = payload.text ?? "";
|
|
340
429
|
try {
|
|
341
430
|
if (text.length > 0) {
|
|
342
|
-
|
|
431
|
+
accumulatedText += text;
|
|
432
|
+
hasSentResponse = true;
|
|
433
|
+
await sendA2AResponse({
|
|
343
434
|
config,
|
|
344
435
|
sessionId,
|
|
345
436
|
taskId: currentTaskId,
|
|
346
437
|
messageId: currentMessageId,
|
|
347
438
|
text,
|
|
348
439
|
append: false,
|
|
440
|
+
final: false,
|
|
441
|
+
log: false,
|
|
349
442
|
});
|
|
350
443
|
}
|
|
351
444
|
}
|
|
352
445
|
catch (err) {
|
|
353
|
-
|
|
446
|
+
scopedLog().error(`[PARTIAL-REPLY] Failed to send partial reply:`, err);
|
|
354
447
|
}
|
|
355
448
|
},
|
|
356
449
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { XYWebSocketManager } from "./websocket.js";
|
|
2
|
-
export declare function handleSelfEvolutionEvent(context: any,
|
|
2
|
+
export declare function handleSelfEvolutionEvent(context: any, cfg: any): Promise<void>;
|
|
3
3
|
/**
|
|
4
4
|
* 读取 .xiaoyiruntime 中的 selfEvolutionState 并直接通过 wsManager 下发指令回复设备
|
|
5
5
|
* 参考trigger实现:直接使用当前已连接的 wsManager 发送消息,避免 getXYWebSocketManager 返回未连接实例
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync } from "fs";
|
|
2
2
|
import { v4 as uuidv4 } from "uuid";
|
|
3
|
+
import { resolveXYConfig } from "./config.js";
|
|
4
|
+
import { sendA2AResponse } from "./formatter.js";
|
|
3
5
|
import { logger } from "./utils/logger.js";
|
|
4
6
|
const XIAOYIRUNTIME_PATH = "/home/sandbox/.openclaw/.xiaoyiruntime";
|
|
5
|
-
export function handleSelfEvolutionEvent(context,
|
|
7
|
+
export async function handleSelfEvolutionEvent(context, cfg) {
|
|
6
8
|
try {
|
|
7
9
|
const state = context.event?.payload?.selfEvolutionState;
|
|
8
10
|
if (typeof state !== "string") {
|
|
9
11
|
logger.error("[SELF_EVOLUTION] invalid payload: missing selfEvolutionState");
|
|
10
12
|
return;
|
|
11
13
|
}
|
|
14
|
+
const sessionId = context.sessionId ?? "";
|
|
15
|
+
const taskId = context.taskId ?? sessionId;
|
|
16
|
+
const messageId = context.messageId ?? uuidv4();
|
|
17
|
+
const config = resolveXYConfig(cfg);
|
|
12
18
|
logger.log(`[SELF_EVOLUTION] received state: ${state}`);
|
|
13
19
|
let content;
|
|
14
20
|
try {
|
|
@@ -19,6 +25,8 @@ export function handleSelfEvolutionEvent(context, runtime) {
|
|
|
19
25
|
logger.log(`[SELF_EVOLUTION] ${XIAOYIRUNTIME_PATH} not found, creating new file`);
|
|
20
26
|
writeFileSync(XIAOYIRUNTIME_PATH, `selfEvolutionState=${state}\n`, "utf-8");
|
|
21
27
|
logger.log(`[SELF_EVOLUTION] wrote selfEvolutionState=${state}`);
|
|
28
|
+
await sendA2AResponse({ config, sessionId, taskId, messageId, text: "", append: false, final: true });
|
|
29
|
+
logger.log(`[SELF_EVOLUTION] Sent final response (empty, stream end)`);
|
|
22
30
|
return;
|
|
23
31
|
}
|
|
24
32
|
const lines = content.split("\n");
|
|
@@ -40,6 +48,9 @@ export function handleSelfEvolutionEvent(context, runtime) {
|
|
|
40
48
|
writeFileSync(XIAOYIRUNTIME_PATH, updated.join("\n"), "utf-8");
|
|
41
49
|
}
|
|
42
50
|
logger.log(`[SELF_EVOLUTION] updated selfEvolutionState=${state} in ${XIAOYIRUNTIME_PATH}`);
|
|
51
|
+
// Reply with final empty response to acknowledge the state update
|
|
52
|
+
await sendA2AResponse({ config, sessionId, taskId, messageId, text: "", append: false, final: true });
|
|
53
|
+
logger.log(`[SELF_EVOLUTION] Sent final response (empty, stream end)`);
|
|
43
54
|
}
|
|
44
55
|
catch (err) {
|
|
45
56
|
logger.error("[SELF_EVOLUTION] failed to handle event:", err);
|
|
@@ -108,7 +119,7 @@ export async function handleSelfEvolutionStateGetEvent(context, cfg, runtime, ws
|
|
|
108
119
|
kind: "artifact-update",
|
|
109
120
|
append: false,
|
|
110
121
|
lastChunk: true,
|
|
111
|
-
final:
|
|
122
|
+
final: true,
|
|
112
123
|
artifact: {
|
|
113
124
|
artifactId: uuidv4(),
|
|
114
125
|
parts: [{
|
|
@@ -127,7 +138,7 @@ export async function handleSelfEvolutionStateGetEvent(context, cfg, runtime, ws
|
|
|
127
138
|
taskId,
|
|
128
139
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
129
140
|
};
|
|
130
|
-
logger.log(`[A2A_COMMAND]
|
|
141
|
+
logger.log(`[A2A_COMMAND] Sending A2A command, taskId: ${taskId}`);
|
|
131
142
|
await wsManager.sendMessage(sessionId, outboundMessage);
|
|
132
143
|
logger.log(`[SELF_EVOLUTION_GET] command sent successfully`);
|
|
133
144
|
}
|