@ynhcj/xiaoyi-channel 0.0.150-beta → 0.0.151-beta
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 +3 -69
- package/dist/src/approval-bridge.d.ts +48 -0
- package/dist/src/approval-bridge.js +382 -0
- package/dist/src/bot.js +64 -68
- package/dist/src/client.js +13 -23
- 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 +80 -70
- package/dist/src/cspl/configs.json +10 -0
- package/dist/src/cspl/constants.d.ts +46 -24
- package/dist/src/cspl/constants.js +41 -16
- package/dist/src/cspl/sentinel_hook.d.ts +2 -0
- package/dist/src/cspl/sentinel_hook.js +84 -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 +11 -2
- package/dist/src/cspl/utils.js +265 -15
- package/dist/src/formatter.js +92 -37
- package/dist/src/monitor.js +18 -21
- package/dist/src/outbound.js +8 -9
- package/dist/src/push.js +8 -15
- package/dist/src/reply-dispatcher.js +39 -48
- package/dist/src/self-evolution-handler.js +1 -1
- 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 +138 -0
- package/dist/src/tools/calendar-tool.js +1 -1
- package/dist/src/tools/call-device-tool.js +3 -0
- package/dist/src/tools/call-phone-tool.js +1 -1
- package/dist/src/tools/create-alarm-tool.js +1 -1
- package/dist/src/tools/create-all-tools.js +5 -1
- package/dist/src/tools/delete-alarm-tool.js +1 -1
- package/dist/src/tools/find-pc-devices-tool.d.ts +2 -1
- package/dist/src/tools/find-pc-devices-tool.js +84 -88
- package/dist/src/tools/get-device-file-tool-schema.js +3 -2
- package/dist/src/tools/location-tool.js +1 -1
- package/dist/src/tools/modify-alarm-tool.js +1 -1
- package/dist/src/tools/modify-note-tool.js +1 -1
- package/dist/src/tools/note-tool.js +1 -1
- package/dist/src/tools/query-app-message-tool.js +1 -1
- package/dist/src/tools/query-memory-data-tool.js +1 -1
- package/dist/src/tools/query-todo-task-tool.js +1 -1
- package/dist/src/tools/save-file-to-phone-tool.js +1 -1
- package/dist/src/tools/save-media-to-gallery-tool.js +1 -1
- package/dist/src/tools/search-alarm-tool.js +1 -1
- package/dist/src/tools/search-calendar-tool.js +1 -1
- package/dist/src/tools/search-contact-tool.js +1 -1
- package/dist/src/tools/search-email-tool.js +1 -1
- package/dist/src/tools/search-file-tool.js +11 -8
- package/dist/src/tools/search-message-tool.js +1 -1
- package/dist/src/tools/search-note-tool.js +1 -1
- package/dist/src/tools/search-photo-gallery-tool.js +1 -1
- package/dist/src/tools/send-email-tool.js +1 -1
- package/dist/src/tools/send-file-to-user-tool.js +2 -2
- package/dist/src/tools/send-message-tool.js +1 -1
- package/dist/src/tools/session-manager.js +5 -0
- package/dist/src/tools/upload-file-tool.js +15 -5
- package/dist/src/tools/upload-photo-tool.js +1 -1
- package/dist/src/tools/xiaoyi-add-collection-tool.js +1 -1
- package/dist/src/tools/xiaoyi-collection-tool.js +1 -1
- package/dist/src/tools/xiaoyi-delete-collection-tool.js +1 -1
- package/dist/src/tools/xiaoyi-gui-tool.js +1 -1
- package/dist/src/trigger-handler.js +4 -7
- 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.js +37 -25
- package/package.json +1 -1
package/dist/src/monitor.js
CHANGED
|
@@ -51,7 +51,7 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
51
51
|
}
|
|
52
52
|
: undefined;
|
|
53
53
|
// 🔍 Diagnose WebSocket managers before gateway start
|
|
54
|
-
logger.log("
|
|
54
|
+
logger.log("[DIAGNOSTICS] Checking WebSocket managers before gateway start...");
|
|
55
55
|
diagnoseAllManagers();
|
|
56
56
|
// Get WebSocket manager (cached)
|
|
57
57
|
const wsManager = getXYWebSocketManager(account, runtime);
|
|
@@ -77,12 +77,12 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
77
77
|
// Event handlers (defined early so they can be referenced in cleanup)
|
|
78
78
|
const messageHandler = (message, sessionId, serverId) => {
|
|
79
79
|
const messageKey = `${sessionId}::${message.id}`;
|
|
80
|
-
logger.log(`[MONITOR-HANDLER]
|
|
80
|
+
logger.log(`[MONITOR-HANDLER] messageHandler triggered: messageId=${message.id}`);
|
|
81
81
|
// ✅ Report health: received a message
|
|
82
82
|
trackEvent?.();
|
|
83
83
|
// Check for duplicate message handling
|
|
84
84
|
if (activeMessages.has(messageKey)) {
|
|
85
|
-
logger.error(`[MONITOR-HANDLER]
|
|
85
|
+
logger.error(`[MONITOR-HANDLER] WARNING: Duplicate message detected, messageKey=${messageKey}`);
|
|
86
86
|
}
|
|
87
87
|
activeMessages.add(messageKey);
|
|
88
88
|
const task = async () => {
|
|
@@ -119,8 +119,7 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
119
119
|
const hasActiveRun = hasActiveTask(parsed.sessionId);
|
|
120
120
|
if (steerMode && hasActiveRun) {
|
|
121
121
|
// Steer模式且有活跃任务:不入队列,直接并发执行
|
|
122
|
-
logger.log(`[MONITOR-HANDLER]
|
|
123
|
-
logger.log(`[MONITOR-HANDLER] - sessionId: ${parsed.sessionId}`);
|
|
122
|
+
logger.log(`[MONITOR-HANDLER] STEER MODE: Executing concurrently for messageKey=${messageKey}`);
|
|
124
123
|
void task().catch((err) => {
|
|
125
124
|
logger.error(`XY gateway: concurrent steer task failed for ${messageKey}: ${String(err)}`);
|
|
126
125
|
activeMessages.delete(messageKey);
|
|
@@ -129,7 +128,7 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
129
128
|
else {
|
|
130
129
|
// 正常模式:入队列串行执行
|
|
131
130
|
void enqueue(sessionId, task).catch((err) => {
|
|
132
|
-
logger.error(`XY gateway: queue processing failed
|
|
131
|
+
logger.error(`XY gateway: queue processing failed: ${String(err)}`);
|
|
133
132
|
activeMessages.delete(messageKey);
|
|
134
133
|
});
|
|
135
134
|
}
|
|
@@ -138,7 +137,7 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
138
137
|
// 解析失败,回退到正常队列模式
|
|
139
138
|
logger.error(`[MONITOR-HANDLER] Failed to parse message for steer detection: ${String(parseErr)}`);
|
|
140
139
|
void enqueue(sessionId, task).catch((err) => {
|
|
141
|
-
logger.error(`XY gateway: queue processing failed
|
|
140
|
+
logger.error(`XY gateway: queue processing failed: ${String(err)}`);
|
|
142
141
|
activeMessages.delete(messageKey);
|
|
143
142
|
});
|
|
144
143
|
}
|
|
@@ -164,9 +163,7 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
164
163
|
logger.error(`XY gateway: ${serverId} error: ${String(err)}`);
|
|
165
164
|
};
|
|
166
165
|
const triggerEventHandler = (context) => {
|
|
167
|
-
logger.log(`[MONITOR]
|
|
168
|
-
logger.log(`[MONITOR] - sessionId: ${context.sessionId}`);
|
|
169
|
-
logger.log(`[MONITOR] - taskId: ${context.taskId}`);
|
|
166
|
+
logger.log(`[MONITOR] Received trigger-event, dispatching to handler...`);
|
|
170
167
|
// 异步处理 Trigger 事件,不阻塞主流程
|
|
171
168
|
handleTriggerEvent(context, cfg, runtime, accountId).catch((err) => {
|
|
172
169
|
logger.error(`[MONITOR] Failed to handle trigger-event:`, err);
|
|
@@ -189,13 +186,13 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
189
186
|
const cleanup = () => {
|
|
190
187
|
logger.log("XY gateway: cleaning up...");
|
|
191
188
|
// 🔍 Diagnose before cleanup
|
|
192
|
-
logger.log("
|
|
189
|
+
logger.log("[DIAGNOSTICS] Checking WebSocket managers before cleanup...");
|
|
193
190
|
diagnoseAllManagers();
|
|
194
191
|
// Stop health check interval
|
|
195
192
|
if (healthCheckInterval) {
|
|
196
193
|
clearInterval(healthCheckInterval);
|
|
197
194
|
healthCheckInterval = null;
|
|
198
|
-
logger.log("
|
|
195
|
+
logger.log("Stopped periodic health check");
|
|
199
196
|
}
|
|
200
197
|
// Remove event handlers to prevent duplicate calls on gateway restart
|
|
201
198
|
wsManager.off("message", messageHandler);
|
|
@@ -215,9 +212,9 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
215
212
|
cleanupAllSessions();
|
|
216
213
|
loggedServers.clear();
|
|
217
214
|
activeMessages.clear();
|
|
218
|
-
logger.log(`[MONITOR-HANDLER]
|
|
215
|
+
logger.log(`[MONITOR-HANDLER] Cleanup complete, cleared active messages and sessions`);
|
|
219
216
|
// 🔍 Diagnose after cleanup
|
|
220
|
-
logger.log("
|
|
217
|
+
logger.log("[DIAGNOSTICS] Checking WebSocket managers after cleanup...");
|
|
221
218
|
diagnoseAllManagers();
|
|
222
219
|
};
|
|
223
220
|
const handleAbort = async () => {
|
|
@@ -228,7 +225,7 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
228
225
|
if (activeBindings.length > 0) {
|
|
229
226
|
const config = resolveXYConfig(cfg);
|
|
230
227
|
const notificationText = "Gateway即将重启,重启期间可能短暂出现\u201c环境异常\u201d提示,请稍候并耐心重试~";
|
|
231
|
-
logger.log(`[MONITOR]
|
|
228
|
+
logger.log(`[MONITOR] Sending restart notifications to ${activeBindings.length} active session(s)`);
|
|
232
229
|
const sendPromises = activeBindings.map(binding => sendA2AResponse({
|
|
233
230
|
config,
|
|
234
231
|
sessionId: binding.sessionId,
|
|
@@ -238,10 +235,10 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
238
235
|
append: false,
|
|
239
236
|
final: true,
|
|
240
237
|
}).catch(err => {
|
|
241
|
-
logger.error(`[MONITOR] Failed to send restart notification
|
|
238
|
+
logger.error(`[MONITOR] Failed to send restart notification: ${String(err)}`);
|
|
242
239
|
}));
|
|
243
240
|
await Promise.all(sendPromises);
|
|
244
|
-
logger.log(`[MONITOR]
|
|
241
|
+
logger.log(`[MONITOR] Restart notifications sent to ${activeBindings.length} session(s)`);
|
|
245
242
|
}
|
|
246
243
|
else {
|
|
247
244
|
logger.log(`[MONITOR] No active sessions, skipping restart notifications`);
|
|
@@ -270,20 +267,20 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
270
267
|
wsManager.on("self-evolution-state-get-event", selfEvolutionStateGetHandler);
|
|
271
268
|
wsManager.on("login-token-event", loginTokenEventHandler);
|
|
272
269
|
// Start periodic health check (every 6 hours)
|
|
273
|
-
logger.log("
|
|
270
|
+
logger.log("Starting periodic health check (every 6 hours)...");
|
|
274
271
|
healthCheckInterval = setInterval(() => {
|
|
275
|
-
logger.log("
|
|
272
|
+
logger.log("[HEALTH CHECK] Periodic WebSocket diagnostics...");
|
|
276
273
|
diagnoseAllManagers();
|
|
277
274
|
// Auto-cleanup orphan connections
|
|
278
275
|
const cleaned = cleanupOrphanConnections();
|
|
279
276
|
if (cleaned > 0) {
|
|
280
|
-
logger.log(
|
|
277
|
+
logger.log(`[HEALTH CHECK] Auto-cleaned ${cleaned} manager(s) with orphan connections`);
|
|
281
278
|
}
|
|
282
279
|
// Cleanup stale sessions (older than 10min TTL)
|
|
283
280
|
const cleanedSessions = cleanupStaleSessions();
|
|
284
281
|
const remainingSessions = getActiveSessionCount();
|
|
285
282
|
if (cleanedSessions > 0 || remainingSessions > 0) {
|
|
286
|
-
logger.log(
|
|
283
|
+
logger.log(`[HEALTH CHECK] Sessions: cleaned=${cleanedSessions}, active=${remainingSessions}`);
|
|
287
284
|
}
|
|
288
285
|
// Cleanup stale temp files (older than 24 hours)
|
|
289
286
|
void cleanupStaleTempFiles();
|
package/dist/src/outbound.js
CHANGED
|
@@ -106,10 +106,10 @@ export const xyOutbound = {
|
|
|
106
106
|
let pushDataId;
|
|
107
107
|
try {
|
|
108
108
|
pushDataId = await savePushData(text);
|
|
109
|
-
logger.log(`[xyOutbound.sendText]
|
|
109
|
+
logger.log(`[xyOutbound.sendText] Push data saved with ID: ${pushDataId.substring(0, 20)}`);
|
|
110
110
|
}
|
|
111
111
|
catch (error) {
|
|
112
|
-
logger.error(`[xyOutbound.sendText]
|
|
112
|
+
logger.error(`[xyOutbound.sendText] Failed to save push data:`, error);
|
|
113
113
|
// 如果持久化失败,仍然继续发送(不阻塞主流程)
|
|
114
114
|
pushDataId = "";
|
|
115
115
|
}
|
|
@@ -118,14 +118,14 @@ export const xyOutbound = {
|
|
|
118
118
|
let pushIdList = [];
|
|
119
119
|
try {
|
|
120
120
|
pushIdList = await getAllPushIds();
|
|
121
|
-
logger.log(`[xyOutbound.sendText]
|
|
121
|
+
logger.log(`[xyOutbound.sendText] Loaded ${pushIdList.length} pushIds`);
|
|
122
122
|
}
|
|
123
123
|
catch (error) {
|
|
124
|
-
logger.error(`[xyOutbound.sendText]
|
|
124
|
+
logger.error(`[xyOutbound.sendText] Failed to load pushIds:`, error);
|
|
125
125
|
}
|
|
126
126
|
// 3. 如果 pushIdList 为空,回退到原有逻辑(使用 config pushId)
|
|
127
127
|
if (pushIdList.length === 0) {
|
|
128
|
-
logger.log(`[xyOutbound.sendText]
|
|
128
|
+
logger.log(`[xyOutbound.sendText] No pushIds found, falling back to config pushId`);
|
|
129
129
|
pushIdList = [config.pushId];
|
|
130
130
|
}
|
|
131
131
|
// Create push service
|
|
@@ -135,7 +135,7 @@ export const xyOutbound = {
|
|
|
135
135
|
// Truncate push content to max length 1000
|
|
136
136
|
const pushText = text.length > 1000 ? text.slice(0, 1000) : text;
|
|
137
137
|
// 4. 遍历所有 pushId,依次发送推送通知
|
|
138
|
-
logger.log(`[xyOutbound.sendText]
|
|
138
|
+
logger.log(`[xyOutbound.sendText] Broadcasting to ${pushIdList.length} pushId(s)...`);
|
|
139
139
|
let successCount = 0;
|
|
140
140
|
let failureCount = 0;
|
|
141
141
|
for (const pushId of pushIdList) {
|
|
@@ -143,11 +143,11 @@ export const xyOutbound = {
|
|
|
143
143
|
// 传入 pushId 和 pushDataId,使用 kind="data" 格式
|
|
144
144
|
await pushService.sendPush(pushText, title, undefined, actualTo, pushDataId, pushId);
|
|
145
145
|
successCount++;
|
|
146
|
-
logger.log(`[xyOutbound.sendText]
|
|
146
|
+
logger.log(`[xyOutbound.sendText] Sent successfully to pushId: ${pushId.substring(0, 20)}...`);
|
|
147
147
|
}
|
|
148
148
|
catch (error) {
|
|
149
149
|
failureCount++;
|
|
150
|
-
logger.error(`[xyOutbound.sendText]
|
|
150
|
+
logger.error(`[xyOutbound.sendText] Failed to send to pushId: ${pushId.substring(0, 20)}...`, error);
|
|
151
151
|
// 单个 pushId 发送失败不影响其他,继续处理下一个
|
|
152
152
|
}
|
|
153
153
|
}
|
|
@@ -184,7 +184,6 @@ export const xyOutbound = {
|
|
|
184
184
|
}
|
|
185
185
|
logger.log(`[xyOutbound.sendMedia] File uploaded:`, {
|
|
186
186
|
fileId,
|
|
187
|
-
sessionId,
|
|
188
187
|
taskId,
|
|
189
188
|
});
|
|
190
189
|
// Get filename and mime type from mediaUrl
|
package/dist/src/push.js
CHANGED
|
@@ -34,8 +34,7 @@ export class XYPushService {
|
|
|
34
34
|
const traceId = this.generateTraceId();
|
|
35
35
|
// Use provided pushId or fall back to config pushId
|
|
36
36
|
const actualPushId = pushId || this.config.pushId;
|
|
37
|
-
logger.log(`[PUSH]
|
|
38
|
-
logger.log(`[PUSH] - Using pushId: ${actualPushId.substring(0, 20)}...`);
|
|
37
|
+
logger.log(`[PUSH] Preparing to send push message with pushId: ${actualPushId.substring(0, 20)}...`);
|
|
39
38
|
try {
|
|
40
39
|
const requestBody = {
|
|
41
40
|
jsonrpc: "2.0",
|
|
@@ -81,12 +80,10 @@ export class XYPushService {
|
|
|
81
80
|
body: JSON.stringify(requestBody),
|
|
82
81
|
});
|
|
83
82
|
// Log response status and headers
|
|
84
|
-
logger.log(`[PUSH]
|
|
85
|
-
logger.log(`[PUSH] - HTTP Status: ${response.status} ${response.statusText}`);
|
|
83
|
+
logger.log(`[PUSH] Response received, HTTP Status: ${response.status} ${response.statusText}`);
|
|
86
84
|
if (!response.ok) {
|
|
87
85
|
const errorText = await response.text();
|
|
88
|
-
logger.error(`[PUSH]
|
|
89
|
-
logger.error(`[PUSH] - HTTP Status: ${response.status}`);
|
|
86
|
+
logger.error(`[PUSH] Push request failed, HTTP Status: ${response.status}`);
|
|
90
87
|
throw new Error(`Push failed: HTTP ${response.status} - ${errorText}`);
|
|
91
88
|
}
|
|
92
89
|
// Try to parse JSON response with detailed error handling
|
|
@@ -94,7 +91,7 @@ export class XYPushService {
|
|
|
94
91
|
try {
|
|
95
92
|
const responseText = await response.text();
|
|
96
93
|
if (!responseText || responseText.trim() === '') {
|
|
97
|
-
logger.error(`[PUSH]
|
|
94
|
+
logger.error(`[PUSH] Received empty response body`);
|
|
98
95
|
result = {};
|
|
99
96
|
}
|
|
100
97
|
else {
|
|
@@ -102,21 +99,17 @@ export class XYPushService {
|
|
|
102
99
|
}
|
|
103
100
|
}
|
|
104
101
|
catch (parseError) {
|
|
105
|
-
logger.error(`[PUSH]
|
|
106
|
-
logger.error(`[PUSH] - Parse error: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
|
|
102
|
+
logger.error(`[PUSH] Failed to parse JSON response: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
|
|
107
103
|
throw new Error(`Invalid JSON response from push service: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
|
|
108
104
|
}
|
|
109
|
-
logger.log(`[PUSH]
|
|
110
|
-
logger.log(`[PUSH] - Trace ID: ${traceId}`);
|
|
105
|
+
logger.log(`[PUSH] Push message sent successfully, Trace ID: ${traceId}`);
|
|
111
106
|
}
|
|
112
107
|
catch (error) {
|
|
113
|
-
logger.error(`[PUSH] ❌ Failed to send push message`);
|
|
114
108
|
if (error instanceof Error) {
|
|
115
|
-
logger.error(`[PUSH]
|
|
116
|
-
logger.error(`[PUSH] - Error message: ${error.message}`);
|
|
109
|
+
logger.error(`[PUSH] Failed to send push message: ${error.name} - ${error.message}`);
|
|
117
110
|
}
|
|
118
111
|
else {
|
|
119
|
-
logger.error(`[PUSH]
|
|
112
|
+
logger.error(`[PUSH] Failed to send push message:`, error);
|
|
120
113
|
}
|
|
121
114
|
throw error;
|
|
122
115
|
}
|
|
@@ -32,11 +32,11 @@ export async function cleanupStaleTempFiles(tempDir = "/tmp/xy_channel") {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
if (cleanedCount > 0) {
|
|
35
|
-
logger.log(`[CLEANUP]
|
|
35
|
+
logger.log(`[CLEANUP] Cleaned ${cleanedCount} stale files (>${TEMP_FILE_TTL_MS / 1000 / 3600}h) from ${tempDir}`);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
catch (err) {
|
|
39
|
-
logger.error(`[CLEANUP]
|
|
39
|
+
logger.error(`[CLEANUP] Failed to cleanup temp dir:`, err);
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
@@ -46,21 +46,19 @@ export async function cleanupStaleTempFiles(tempDir = "/tmp/xy_channel") {
|
|
|
46
46
|
*/
|
|
47
47
|
export function createXYReplyDispatcher(params) {
|
|
48
48
|
const { cfg, runtime, sessionId, taskId, messageId, accountId, steerState } = params;
|
|
49
|
-
logger.log(`[DISPATCHER-CREATE] ******* Creating dispatcher *******`);
|
|
50
|
-
logger.log(`[DISPATCHER-CREATE] - taskId: ${taskId}`);
|
|
51
49
|
// 初始taskId和messageId(作为fallback)
|
|
52
50
|
const initialTaskId = taskId;
|
|
53
51
|
const initialMessageId = messageId;
|
|
54
|
-
/**
|
|
55
|
-
* 🔑 核心改造:动态获取当前活跃的taskId和messageId
|
|
56
|
-
* 每次需要taskId时,都从TaskManager获取最新值
|
|
57
|
-
*/
|
|
58
52
|
const getActiveTaskId = () => {
|
|
59
53
|
return getCurrentTaskId(sessionId) ?? initialTaskId;
|
|
60
54
|
};
|
|
61
55
|
const getActiveMessageId = () => {
|
|
62
56
|
return getCurrentMessageId(sessionId) ?? initialMessageId;
|
|
63
57
|
};
|
|
58
|
+
// Create a scoped logger that always uses this session's sessionId
|
|
59
|
+
// and dynamically resolves the latest taskId
|
|
60
|
+
const scopedLog = () => logger.withContext(sessionId, getActiveTaskId());
|
|
61
|
+
scopedLog().log(`[DISPATCHER-CREATE] Creating dispatcher`);
|
|
64
62
|
const core = getXYRuntime();
|
|
65
63
|
const config = resolveXYConfig(cfg);
|
|
66
64
|
// Simplified prefix context for single-account Xiaoyi channel
|
|
@@ -77,14 +75,12 @@ export function createXYReplyDispatcher(params) {
|
|
|
77
75
|
* Start the status update interval
|
|
78
76
|
*/
|
|
79
77
|
const startStatusInterval = () => {
|
|
80
|
-
|
|
78
|
+
scopedLog().log(`[STATUS-INTERVAL] Starting interval`);
|
|
81
79
|
statusUpdateInterval = setInterval(() => {
|
|
82
80
|
// 🔑 使用动态taskId
|
|
83
81
|
const currentTaskId = getActiveTaskId();
|
|
84
82
|
const currentMessageId = getActiveMessageId();
|
|
85
|
-
|
|
86
|
-
logger.log(`[STATUS INTERVAL] - sessionId: ${sessionId}`);
|
|
87
|
-
logger.log(`[STATUS INTERVAL] - currentTaskId: ${currentTaskId}`);
|
|
83
|
+
scopedLog().log(`[STATUS-INTERVAL] Triggering status update, taskId=${currentTaskId}`);
|
|
88
84
|
void sendStatusUpdate({
|
|
89
85
|
config,
|
|
90
86
|
sessionId,
|
|
@@ -93,13 +89,13 @@ export function createXYReplyDispatcher(params) {
|
|
|
93
89
|
text: "任务正在处理中,请稍候~",
|
|
94
90
|
state: "working",
|
|
95
91
|
}).catch((err) => {
|
|
96
|
-
|
|
92
|
+
scopedLog().error(`Failed to send status update:`, err);
|
|
97
93
|
});
|
|
98
94
|
}, 30000); // 30 seconds
|
|
99
95
|
};
|
|
100
96
|
const stopStatusInterval = () => {
|
|
101
97
|
if (statusUpdateInterval) {
|
|
102
|
-
|
|
98
|
+
scopedLog().log(`[STATUS-INTERVAL] Stopping interval`);
|
|
103
99
|
clearInterval(statusUpdateInterval);
|
|
104
100
|
statusUpdateInterval = null;
|
|
105
101
|
}
|
|
@@ -110,26 +106,26 @@ export function createXYReplyDispatcher(params) {
|
|
|
110
106
|
humanDelay: core.channel.reply.resolveHumanDelayConfig(cfg, accountId),
|
|
111
107
|
onReplyStart: () => {
|
|
112
108
|
const currentTaskId = getActiveTaskId();
|
|
113
|
-
|
|
109
|
+
scopedLog().log(`[REPLY-START] Reply started, taskId=${currentTaskId}, steered=${steerState.steered}`);
|
|
114
110
|
},
|
|
115
111
|
deliver: async (payload, info) => {
|
|
116
112
|
// 🔑 steered dispatch不发送内容(让主dispatcher处理)
|
|
117
113
|
if (steerState.steered) {
|
|
118
|
-
|
|
114
|
+
scopedLog().log(`[DELIVER] Steered dispatch, skipping, kind=${info?.kind}`);
|
|
119
115
|
return;
|
|
120
116
|
}
|
|
121
117
|
const text = payload.text ?? "";
|
|
122
118
|
const currentTaskId = getActiveTaskId();
|
|
123
119
|
const currentMessageId = getActiveMessageId();
|
|
124
|
-
|
|
120
|
+
scopedLog().log(`[DELIVER] kind=${info?.kind}, text.length=${text.length}`);
|
|
125
121
|
try {
|
|
126
122
|
if (!text.trim()) {
|
|
127
|
-
|
|
123
|
+
scopedLog().log(`[DELIVER SKIP] Empty text, skipping`);
|
|
128
124
|
return;
|
|
129
125
|
}
|
|
130
126
|
accumulatedText += text;
|
|
131
127
|
hasSentResponse = true;
|
|
132
|
-
|
|
128
|
+
scopedLog().log(`[DELIVER] Accumulated text, length=${accumulatedText.length}`);
|
|
133
129
|
// 🔑 使用动态taskId发送reasoningText更新
|
|
134
130
|
await sendReasoningTextUpdate({
|
|
135
131
|
config,
|
|
@@ -138,10 +134,10 @@ export function createXYReplyDispatcher(params) {
|
|
|
138
134
|
messageId: currentMessageId,
|
|
139
135
|
text,
|
|
140
136
|
});
|
|
141
|
-
|
|
137
|
+
scopedLog().log(`[DELIVER] Sent deliver text as reasoningText update`);
|
|
142
138
|
}
|
|
143
139
|
catch (deliverError) {
|
|
144
|
-
|
|
140
|
+
scopedLog().error(`Failed to deliver message:`, deliverError);
|
|
145
141
|
}
|
|
146
142
|
},
|
|
147
143
|
onError: async (err, info) => {
|
|
@@ -149,7 +145,7 @@ export function createXYReplyDispatcher(params) {
|
|
|
149
145
|
stopStatusInterval();
|
|
150
146
|
// 🔑 steered dispatcher不发送错误状态(让主dispatcher处理)
|
|
151
147
|
if (steerState.steered) {
|
|
152
|
-
|
|
148
|
+
scopedLog().log(`[ON-ERROR] Steered dispatch, skipping error response`);
|
|
153
149
|
return;
|
|
154
150
|
}
|
|
155
151
|
if (!hasSentResponse) {
|
|
@@ -166,28 +162,23 @@ export function createXYReplyDispatcher(params) {
|
|
|
166
162
|
});
|
|
167
163
|
}
|
|
168
164
|
catch (statusError) {
|
|
169
|
-
|
|
165
|
+
scopedLog().error(`Failed to send error status:`, statusError);
|
|
170
166
|
}
|
|
171
167
|
}
|
|
172
168
|
},
|
|
173
169
|
onIdle: async () => {
|
|
174
170
|
const currentTaskId = getActiveTaskId();
|
|
175
171
|
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}`);
|
|
172
|
+
scopedLog().log(`[ON-IDLE] Reply idle, steered=${steerState.steered}, hasSentResponse=${hasSentResponse}, finalSent=${finalSent}`);
|
|
182
173
|
// 🔑 steered dispatch不发送final响应(核心已注入到活跃 Pi run)
|
|
183
174
|
if (steerState.steered) {
|
|
184
|
-
|
|
175
|
+
scopedLog().log(`[ON-IDLE] Steered dispatch, skipping final response`);
|
|
185
176
|
stopStatusInterval();
|
|
186
177
|
return; // ← 直接返回,不发送任何东西!
|
|
187
178
|
}
|
|
188
179
|
// 正常模式(或未被steer的dispatch)
|
|
189
180
|
if (hasSentResponse && !finalSent) {
|
|
190
|
-
|
|
181
|
+
scopedLog().log(`[ON-IDLE] Sending accumulated text, length=${accumulatedText.length}`);
|
|
191
182
|
try {
|
|
192
183
|
// 🔑 使用动态taskId发送完成状态
|
|
193
184
|
await sendStatusUpdate({
|
|
@@ -198,7 +189,7 @@ export function createXYReplyDispatcher(params) {
|
|
|
198
189
|
text: "任务处理已完成~",
|
|
199
190
|
state: "completed",
|
|
200
191
|
});
|
|
201
|
-
|
|
192
|
+
scopedLog().log(`[ON-IDLE] Sent completion status update`);
|
|
202
193
|
// 🔑 使用动态taskId发送最终响应
|
|
203
194
|
await sendA2AResponse({
|
|
204
195
|
config,
|
|
@@ -210,15 +201,15 @@ export function createXYReplyDispatcher(params) {
|
|
|
210
201
|
final: true,
|
|
211
202
|
});
|
|
212
203
|
finalSent = true;
|
|
213
|
-
|
|
204
|
+
scopedLog().log(`[ON-IDLE] Sent final response`);
|
|
214
205
|
}
|
|
215
206
|
catch (err) {
|
|
216
|
-
|
|
207
|
+
scopedLog().error(`[ON-IDLE] Failed to send final response:`, err);
|
|
217
208
|
}
|
|
218
209
|
}
|
|
219
210
|
else {
|
|
220
211
|
// 正常失败场景(非steered)
|
|
221
|
-
|
|
212
|
+
scopedLog().log(`[ON-IDLE] Skipping final message: hasSentResponse=${hasSentResponse}, finalSent=${finalSent}`);
|
|
222
213
|
try {
|
|
223
214
|
await sendStatusUpdate({
|
|
224
215
|
config,
|
|
@@ -228,7 +219,7 @@ export function createXYReplyDispatcher(params) {
|
|
|
228
219
|
text: "任务处理中断了~",
|
|
229
220
|
state: "failed",
|
|
230
221
|
});
|
|
231
|
-
|
|
222
|
+
scopedLog().log(`[ON-IDLE] Sent failure status update`);
|
|
232
223
|
await sendA2AResponse({
|
|
233
224
|
config,
|
|
234
225
|
sessionId,
|
|
@@ -241,17 +232,17 @@ export function createXYReplyDispatcher(params) {
|
|
|
241
232
|
errorMessage: "任务执行异常,请重试",
|
|
242
233
|
});
|
|
243
234
|
finalSent = true;
|
|
244
|
-
|
|
235
|
+
scopedLog().log(`[ON-IDLE] Sent error response, code=99921111`);
|
|
245
236
|
}
|
|
246
237
|
catch (err) {
|
|
247
|
-
|
|
238
|
+
scopedLog().error(`[ON-IDLE] Failed to send error response:`, err);
|
|
248
239
|
}
|
|
249
240
|
}
|
|
250
241
|
stopStatusInterval();
|
|
251
242
|
},
|
|
252
243
|
onCleanup: () => {
|
|
253
244
|
const currentTaskId = getActiveTaskId();
|
|
254
|
-
|
|
245
|
+
scopedLog().log(`[ON-CLEANUP] Reply cleanup, steered=${steerState.steered}`);
|
|
255
246
|
},
|
|
256
247
|
});
|
|
257
248
|
return {
|
|
@@ -266,13 +257,13 @@ export function createXYReplyDispatcher(params) {
|
|
|
266
257
|
}
|
|
267
258
|
const currentTaskId = getActiveTaskId();
|
|
268
259
|
const currentMessageId = getActiveMessageId();
|
|
269
|
-
|
|
260
|
+
scopedLog().log(`[TOOL-START] Tool: ${name}, phase: ${phase}`);
|
|
270
261
|
if (phase === "start") {
|
|
271
262
|
const toolName = name || "unknown";
|
|
272
263
|
// call_device_tool 由自身 execute() 内部发送具体子工具名的状态更新
|
|
273
264
|
// get_xxx_tool_schema 是给 LLM 查 schema 用的,无需向用户展示
|
|
274
265
|
if (toolName === "call_device_tool" || toolName.endsWith("_tool_schema") || toolName === "huawei_id_tool") {
|
|
275
|
-
|
|
266
|
+
scopedLog().log(`[TOOL-START] Skipping generic status for ${toolName}`);
|
|
276
267
|
return;
|
|
277
268
|
}
|
|
278
269
|
try {
|
|
@@ -284,10 +275,10 @@ export function createXYReplyDispatcher(params) {
|
|
|
284
275
|
text: `正在使用工具: ${toolName}...`,
|
|
285
276
|
state: "working",
|
|
286
277
|
});
|
|
287
|
-
|
|
278
|
+
scopedLog().log(`[TOOL-START] Sent status update for tool start: ${toolName}`);
|
|
288
279
|
}
|
|
289
280
|
catch (err) {
|
|
290
|
-
|
|
281
|
+
scopedLog().error(`[TOOL-START] Failed to send tool start status:`, err);
|
|
291
282
|
}
|
|
292
283
|
}
|
|
293
284
|
},
|
|
@@ -300,7 +291,7 @@ export function createXYReplyDispatcher(params) {
|
|
|
300
291
|
const currentMessageId = getActiveMessageId();
|
|
301
292
|
const text = payload.text ?? "";
|
|
302
293
|
const hasMedia = Boolean(payload.mediaUrl || (payload.mediaUrls?.length ?? 0) > 0);
|
|
303
|
-
|
|
294
|
+
scopedLog().log(`[TOOL-RESULT] Tool result, text.length: ${text.length}`);
|
|
304
295
|
try {
|
|
305
296
|
if (text.length > 0 || hasMedia) {
|
|
306
297
|
const resultText = text.length > 0 ? text : "工具执行完成";
|
|
@@ -312,11 +303,11 @@ export function createXYReplyDispatcher(params) {
|
|
|
312
303
|
text: resultText,
|
|
313
304
|
state: "working",
|
|
314
305
|
});
|
|
315
|
-
|
|
306
|
+
scopedLog().log(`[TOOL-RESULT] Sent tool result as status update`);
|
|
316
307
|
}
|
|
317
308
|
}
|
|
318
309
|
catch (err) {
|
|
319
|
-
|
|
310
|
+
scopedLog().error(`[TOOL-RESULT] Failed to send tool result status:`, err);
|
|
320
311
|
}
|
|
321
312
|
},
|
|
322
313
|
onReasoningStream: async (payload) => {
|
|
@@ -325,7 +316,7 @@ export function createXYReplyDispatcher(params) {
|
|
|
325
316
|
return;
|
|
326
317
|
}
|
|
327
318
|
const text = payload.text ?? "";
|
|
328
|
-
|
|
319
|
+
scopedLog().log(`[REASONING-STREAM] Reasoning chunk received, text.length: ${text.length}`);
|
|
329
320
|
// Reasoning stream 目前被注释掉
|
|
330
321
|
// 如果需要可以启用
|
|
331
322
|
},
|
|
@@ -350,7 +341,7 @@ export function createXYReplyDispatcher(params) {
|
|
|
350
341
|
}
|
|
351
342
|
}
|
|
352
343
|
catch (err) {
|
|
353
|
-
|
|
344
|
+
scopedLog().error(`[PARTIAL REPLY] Failed to send partial reply:`, err);
|
|
354
345
|
}
|
|
355
346
|
},
|
|
356
347
|
},
|
|
@@ -127,7 +127,7 @@ export async function handleSelfEvolutionStateGetEvent(context, cfg, runtime, ws
|
|
|
127
127
|
taskId,
|
|
128
128
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
129
129
|
};
|
|
130
|
-
logger.log(`[A2A_COMMAND]
|
|
130
|
+
logger.log(`[A2A_COMMAND] Sending A2A command, taskId: ${taskId}`);
|
|
131
131
|
await wsManager.sendMessage(sessionId, outboundMessage);
|
|
132
132
|
logger.log(`[SELF_EVOLUTION_GET] command sent successfully`);
|
|
133
133
|
}
|