@ynhcj/xiaoyi-channel 0.0.50-beta → 0.0.52-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.
Files changed (42) hide show
  1. package/dist/index.js +42 -0
  2. package/dist/src/bot.js +3 -16
  3. package/dist/src/client.js +0 -3
  4. package/dist/src/cspl/call-api.d.ts +3 -0
  5. package/dist/src/cspl/call-api.js +79 -0
  6. package/dist/src/cspl/config.d.ts +19 -0
  7. package/dist/src/cspl/config.js +50 -0
  8. package/dist/src/cspl/constants.d.ts +43 -0
  9. package/dist/src/cspl/constants.js +22 -0
  10. package/dist/src/cspl/utils.d.ts +10 -0
  11. package/dist/src/cspl/utils.js +57 -0
  12. package/dist/src/formatter.js +3 -23
  13. package/dist/src/heartbeat.js +0 -4
  14. package/dist/src/reply-dispatcher.js +32 -0
  15. package/dist/src/steer-injector.d.ts +16 -0
  16. package/dist/src/steer-injector.js +74 -0
  17. package/dist/src/tools/calendar-tool.js +2 -37
  18. package/dist/src/tools/call-phone-tool.js +1 -42
  19. package/dist/src/tools/create-alarm-tool.js +3 -74
  20. package/dist/src/tools/delete-alarm-tool.js +3 -45
  21. package/dist/src/tools/image-reading-tool.js +0 -47
  22. package/dist/src/tools/location-tool.js +1 -32
  23. package/dist/src/tools/modify-alarm-tool.js +3 -77
  24. package/dist/src/tools/modify-note-tool.js +1 -34
  25. package/dist/src/tools/note-tool.js +2 -4
  26. package/dist/src/tools/search-alarm-tool.js +3 -61
  27. package/dist/src/tools/search-calendar-tool.js +2 -39
  28. package/dist/src/tools/search-contact-tool.js +0 -30
  29. package/dist/src/tools/search-file-tool.js +0 -33
  30. package/dist/src/tools/search-message-tool.js +0 -33
  31. package/dist/src/tools/search-note-tool.js +1 -26
  32. package/dist/src/tools/search-photo-gallery-tool.js +2 -31
  33. package/dist/src/tools/send-file-to-user-tool.js +0 -39
  34. package/dist/src/tools/send-message-tool.js +1 -39
  35. package/dist/src/tools/session-manager.js +0 -45
  36. package/dist/src/tools/upload-file-tool.js +0 -49
  37. package/dist/src/tools/upload-photo-tool.js +0 -42
  38. package/dist/src/tools/view-push-result-tool.js +0 -11
  39. package/dist/src/tools/xiaoyi-collection-tool.js +4 -82
  40. package/dist/src/tools/xiaoyi-gui-tool.js +0 -34
  41. package/dist/src/websocket.js +24 -10
  42. package/package.json +1 -1
@@ -1,7 +1,6 @@
1
1
  // Session manager for XY tool context
2
2
  // Stores active session contexts that tools can access
3
3
  import { AsyncLocalStorage } from "async_hooks";
4
- import { logger } from "../utils/logger.js";
5
4
  import { configManager } from "../utils/config-manager.js";
6
5
  import { getCurrentTaskId, getCurrentMessageId } from "../task-manager.js";
7
6
  // Map of sessionKey -> SessionContextWithRef
@@ -13,19 +12,12 @@ const asyncLocalStorage = new AsyncLocalStorage();
13
12
  * Should be called when starting to process a message.
14
13
  */
15
14
  export function registerSession(sessionKey, context) {
16
- logger.log(`[SESSION_MANAGER] 📝 Registering session: ${sessionKey}`);
17
- logger.log(`[SESSION_MANAGER] - sessionId: ${context.sessionId}`);
18
- logger.log(`[SESSION_MANAGER] - taskId: ${context.taskId}`);
19
- logger.log(`[SESSION_MANAGER] - messageId: ${context.messageId}`);
20
- logger.log(`[SESSION_MANAGER] - agentId: ${context.agentId}`);
21
- logger.log(`[SESSION_MANAGER] - Active sessions before: ${activeSessions.size}`);
22
15
  const existing = activeSessions.get(sessionKey);
23
16
  if (existing) {
24
17
  // 更新上下文,增加引用计数
25
18
  existing.taskId = context.taskId;
26
19
  existing.messageId = context.messageId;
27
20
  existing.refCount++;
28
- logger.log(`[SESSION_MANAGER] - Updated existing, refCount=${existing.refCount}`);
29
21
  }
30
22
  else {
31
23
  // 新建
@@ -33,45 +25,30 @@ export function registerSession(sessionKey, context) {
33
25
  ...context,
34
26
  refCount: 1,
35
27
  });
36
- logger.log(`[SESSION_MANAGER] - Created new, refCount=1`);
37
28
  }
38
- logger.log(`[SESSION_MANAGER] - Active sessions after: ${activeSessions.size}`);
39
- logger.log(`[SESSION_MANAGER] - All session keys: [${Array.from(activeSessions.keys()).join(", ")}]`);
40
29
  }
41
30
  /**
42
31
  * Unregister a session context.
43
32
  * Should be called when message processing is complete.
44
33
  */
45
34
  export function unregisterSession(sessionKey) {
46
- logger.log(`[SESSION_MANAGER] 🗑️ Unregistering session: ${sessionKey}`);
47
- logger.log(`[SESSION_MANAGER] - Active sessions before: ${activeSessions.size}`);
48
- logger.log(`[SESSION_MANAGER] - Session existed: ${activeSessions.has(sessionKey)}`);
49
35
  const existing = activeSessions.get(sessionKey);
50
36
  if (!existing) {
51
- logger.log(`[SESSION_MANAGER] - Session not found`);
52
37
  return;
53
38
  }
54
39
  existing.refCount--;
55
- logger.log(`[SESSION_MANAGER] - Decremented refCount: ${existing.refCount}`);
56
40
  if (existing.refCount <= 0) {
57
41
  activeSessions.delete(sessionKey);
58
42
  configManager.clearSession(existing.sessionId);
59
- logger.log(`[SESSION_MANAGER] - Deleted (refCount=0)`);
60
43
  }
61
- logger.log(`[SESSION_MANAGER] - Active sessions after: ${activeSessions.size}`);
62
- logger.log(`[SESSION_MANAGER] - Remaining session keys: [${Array.from(activeSessions.keys()).join(", ")}]`);
63
44
  }
64
45
  /**
65
46
  * Get session context by sessionKey.
66
47
  * Returns null if session not found.
67
48
  */
68
49
  export function getSessionContext(sessionKey) {
69
- logger.log(`[SESSION_MANAGER] 🔍 Getting session by key: ${sessionKey}`);
70
- logger.log(`[SESSION_MANAGER] - Active sessions: ${activeSessions.size}`);
71
50
  const contextWithRef = activeSessions.get(sessionKey) ?? null;
72
- logger.log(`[SESSION_MANAGER] - Found: ${contextWithRef !== null}`);
73
51
  if (contextWithRef) {
74
- logger.log(`[SESSION_MANAGER] - sessionId: ${contextWithRef.sessionId}`);
75
52
  // 返回时去掉refCount字段
76
53
  const { refCount, ...context } = contextWithRef;
77
54
  return context;
@@ -85,20 +62,12 @@ export function getSessionContext(sessionKey) {
85
62
  * Returns null if no sessions are active.
86
63
  */
87
64
  export function getLatestSessionContext() {
88
- logger.log(`[SESSION_MANAGER] 🔍 Getting latest session context`);
89
- logger.log(`[SESSION_MANAGER] - Active sessions count: ${activeSessions.size}`);
90
- logger.log(`[SESSION_MANAGER] - Active session keys: [${Array.from(activeSessions.keys()).join(", ")}]`);
91
65
  if (activeSessions.size === 0) {
92
- logger.error(`[SESSION_MANAGER] - ❌ No active sessions found!`);
93
66
  return null;
94
67
  }
95
68
  // Return the last added session
96
69
  const sessions = Array.from(activeSessions.values());
97
70
  const latestSessionWithRef = sessions[sessions.length - 1];
98
- logger.log(`[SESSION_MANAGER] - ✅ Found latest session:`);
99
- logger.log(`[SESSION_MANAGER] - sessionId: ${latestSessionWithRef.sessionId}`);
100
- logger.log(`[SESSION_MANAGER] - taskId: ${latestSessionWithRef.taskId}`);
101
- logger.log(`[SESSION_MANAGER] - messageId: ${latestSessionWithRef.messageId}`);
102
71
  // 返回时去掉refCount字段
103
72
  const { refCount, ...latestSession } = latestSessionWithRef;
104
73
  return latestSession;
@@ -108,9 +77,6 @@ export function getLatestSessionContext() {
108
77
  * This ensures thread-safe context isolation for concurrent requests.
109
78
  */
110
79
  export function runWithSessionContext(context, callback) {
111
- logger.log(`[SESSION_MANAGER] 🔐 Running with AsyncLocalStorage context`);
112
- logger.log(`[SESSION_MANAGER] - sessionId: ${context.sessionId}`);
113
- logger.log(`[SESSION_MANAGER] - taskId: ${context.taskId}`);
114
80
  return asyncLocalStorage.run(context, callback);
115
81
  }
116
82
  /**
@@ -125,7 +91,6 @@ export function getCurrentSessionContext() {
125
91
  // 1. Get base context from AsyncLocalStorage
126
92
  const context = asyncLocalStorage.getStore() ?? null;
127
93
  if (!context) {
128
- logger.warn(`[SESSION_MANAGER] ⚠️ No session context in AsyncLocalStorage`);
129
94
  return null;
130
95
  }
131
96
  // 2. Get latest taskId and messageId from task-manager
@@ -133,12 +98,6 @@ export function getCurrentSessionContext() {
133
98
  const latestMessageId = getCurrentMessageId(context.sessionId);
134
99
  // 3. If task-manager has a newer taskId, use the latest value
135
100
  if (latestTaskId && latestTaskId !== context.taskId) {
136
- logger.log(`[SESSION_MANAGER] 🔄 TaskId updated (interruption detected)`);
137
- logger.log(`[SESSION_MANAGER] - sessionId: ${context.sessionId}`);
138
- logger.log(`[SESSION_MANAGER] - Old taskId: ${context.taskId}`);
139
- logger.log(`[SESSION_MANAGER] - New taskId: ${latestTaskId}`);
140
- logger.log(`[SESSION_MANAGER] - Old messageId: ${context.messageId}`);
141
- logger.log(`[SESSION_MANAGER] - New messageId: ${latestMessageId ?? context.messageId}`);
142
101
  // Return updated context (create new object, don't modify original)
143
102
  return {
144
103
  ...context,
@@ -147,9 +106,5 @@ export function getCurrentSessionContext() {
147
106
  };
148
107
  }
149
108
  // 4. No update needed, return original context
150
- logger.log(`[SESSION_MANAGER] ✅ Got current session context from AsyncLocalStorage`);
151
- logger.log(`[SESSION_MANAGER] - sessionId: ${context.sessionId}`);
152
- logger.log(`[SESSION_MANAGER] - taskId: ${context.taskId}`);
153
- logger.log(`[SESSION_MANAGER] - messageId: ${context.messageId}`);
154
109
  return context;
155
110
  }
@@ -1,7 +1,6 @@
1
1
  import { getXYWebSocketManager } from "../client.js";
2
2
  import { sendCommand } from "../formatter.js";
3
3
  import { getCurrentSessionContext } from "./session-manager.js";
4
- import { logger } from "../utils/logger.js";
5
4
  /**
6
5
  * XY upload file tool - uploads local files to get publicly accessible URLs.
7
6
  * Requires file URIs from search_file tool as prerequisite.
@@ -41,66 +40,49 @@ export const uploadFileTool = {
41
40
  required: ["fileInfos"],
42
41
  },
43
42
  async execute(toolCallId, params) {
44
- logger.log(`[UPLOAD_FILE_TOOL] 🚀 Starting execution`);
45
- logger.log(`[UPLOAD_FILE_TOOL] - toolCallId: ${toolCallId}`);
46
- logger.log(`[UPLOAD_FILE_TOOL] - params (raw):`, JSON.stringify(params));
47
- logger.log(`[UPLOAD_FILE_TOOL] - params.fileInfos type:`, typeof params.fileInfos);
48
- logger.log(`[UPLOAD_FILE_TOOL] - timestamp: ${new Date().toISOString()}`);
49
43
  // ===== 参数规范化:兼容数组和 JSON 字符串 =====
50
44
  let fileInfos = null;
51
45
  if (!params.fileInfos) {
52
- logger.error(`[UPLOAD_FILE_TOOL] ❌ Missing parameter: fileInfos`);
53
46
  throw new Error("Missing required parameter: fileInfos");
54
47
  }
55
48
  // 情况1: 已经是数组
56
49
  if (Array.isArray(params.fileInfos)) {
57
- logger.log(`[UPLOAD_FILE_TOOL] ✅ fileInfos is already an array`);
58
50
  fileInfos = params.fileInfos;
59
51
  }
60
52
  // 情况2: 是字符串,尝试解析为 JSON 数组
61
53
  else if (typeof params.fileInfos === 'string') {
62
- logger.log(`[UPLOAD_FILE_TOOL] 🔄 fileInfos is a string, attempting to parse as JSON...`);
63
54
  try {
64
55
  const parsed = JSON.parse(params.fileInfos);
65
56
  if (Array.isArray(parsed)) {
66
- logger.log(`[UPLOAD_FILE_TOOL] ✅ Successfully parsed JSON string to array`);
67
57
  fileInfos = parsed;
68
58
  }
69
59
  else {
70
- logger.error(`[UPLOAD_FILE_TOOL] ❌ Parsed JSON is not an array:`, typeof parsed);
71
60
  throw new Error("fileInfos must be an array or a JSON string representing an array");
72
61
  }
73
62
  }
74
63
  catch (parseError) {
75
- logger.error(`[UPLOAD_FILE_TOOL] ❌ Failed to parse fileInfos as JSON:`, parseError);
76
64
  throw new Error(`fileInfos must be a valid JSON array string. Parse error: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
77
65
  }
78
66
  }
79
67
  // 情况3: 其他类型,报错
80
68
  else {
81
- logger.error(`[UPLOAD_FILE_TOOL] ❌ Invalid fileInfos type:`, typeof params.fileInfos);
82
69
  throw new Error(`fileInfos must be an array or a JSON string, got ${typeof params.fileInfos}`);
83
70
  }
84
71
  // 验证数组非空
85
72
  if (!fileInfos || fileInfos.length === 0) {
86
- logger.error(`[UPLOAD_FILE_TOOL] ❌ fileInfos array is empty`);
87
73
  throw new Error("fileInfos array cannot be empty");
88
74
  }
89
- logger.log(`[UPLOAD_FILE_TOOL] ✅ Normalized fileInfos:`, JSON.stringify(fileInfos));
90
75
  // Validate maximum 5 file infos
91
76
  if (fileInfos.length > 5) {
92
- logger.error(`[UPLOAD_FILE_TOOL] ❌ Too many fileInfos: ${fileInfos.length}`);
93
77
  throw new Error(`最多支持 5 条文件信息,当前提供了 ${fileInfos.length} 条。请分批处理。`);
94
78
  }
95
79
  // Validate each fileInfo has required mediaUri field
96
80
  for (let i = 0; i < fileInfos.length; i++) {
97
81
  const fileInfo = fileInfos[i];
98
82
  if (!fileInfo || typeof fileInfo !== 'object') {
99
- logger.error(`[UPLOAD_FILE_TOOL] ❌ fileInfo at index ${i} is not an object`);
100
83
  throw new Error(`fileInfos[${i}] must be an object with mediaUri property`);
101
84
  }
102
85
  if (!fileInfo.mediaUri || typeof fileInfo.mediaUri !== 'string') {
103
- logger.error(`[UPLOAD_FILE_TOOL] ❌ fileInfo at index ${i} missing or invalid mediaUri`);
104
86
  throw new Error(`fileInfos[${i}] must have a valid mediaUri string property`);
105
87
  }
106
88
  // Set default timeout if not provided
@@ -108,28 +90,16 @@ export const uploadFileTool = {
108
90
  fileInfo.timeout = "20000";
109
91
  }
110
92
  }
111
- logger.log(`[UPLOAD_FILE_TOOL] - fileInfos count: ${fileInfos.length}`);
112
93
  // Get session context
113
- logger.log(`[UPLOAD_FILE_TOOL] 🔍 Attempting to get session context...`);
114
94
  const sessionContext = getCurrentSessionContext();
115
95
  if (!sessionContext) {
116
- logger.error(`[UPLOAD_FILE_TOOL] ❌ FAILED: No active session found!`);
117
- logger.error(`[UPLOAD_FILE_TOOL] - toolCallId: ${toolCallId}`);
118
96
  throw new Error("No active XY session found. Upload file tool can only be used during an active conversation.");
119
97
  }
120
- logger.log(`[UPLOAD_FILE_TOOL] ✅ Session context found`);
121
- logger.log(`[UPLOAD_FILE_TOOL] - sessionId: ${sessionContext.sessionId}`);
122
- logger.log(`[UPLOAD_FILE_TOOL] - taskId: ${sessionContext.taskId}`);
123
- logger.log(`[UPLOAD_FILE_TOOL] - messageId: ${sessionContext.messageId}`);
124
98
  const { config, sessionId, taskId, messageId } = sessionContext;
125
99
  // Get WebSocket manager
126
- logger.log(`[UPLOAD_FILE_TOOL] 🔌 Getting WebSocket manager...`);
127
100
  const wsManager = getXYWebSocketManager(config);
128
- logger.log(`[UPLOAD_FILE_TOOL] ✅ WebSocket manager obtained`);
129
101
  // Get public URLs for the files
130
- logger.log(`[UPLOAD_FILE_TOOL] 🌐 Getting public URLs for files...`);
131
102
  const fileUrls = await getFileUrls(wsManager, config, sessionId, taskId, messageId, fileInfos);
132
- logger.log(`[UPLOAD_FILE_TOOL] 🎉 Successfully retrieved ${fileUrls.length} file URLs`);
133
103
  return {
134
104
  content: [
135
105
  {
@@ -149,7 +119,6 @@ export const uploadFileTool = {
149
119
  * Returns array of publicly accessible file URLs
150
120
  */
151
121
  async function getFileUrls(wsManager, config, sessionId, taskId, messageId, fileInfos) {
152
- logger.log(`[UPLOAD_FILE_TOOL] 📦 Building FileUploadForClaw command...`);
153
122
  const command = {
154
123
  header: {
155
124
  namespace: "Common",
@@ -185,26 +154,18 @@ async function getFileUrls(wsManager, config, sessionId, taskId, messageId, file
185
154
  };
186
155
  return new Promise((resolve, reject) => {
187
156
  const timeout = setTimeout(() => {
188
- logger.error(`[UPLOAD_FILE_TOOL] ⏰ Timeout: No response for FileUploadForClaw within 60 seconds`);
189
157
  wsManager.off("data-event", handler);
190
158
  reject(new Error("获取文件URL超时(60秒)"));
191
159
  }, 60000);
192
160
  const handler = (event) => {
193
- logger.log(`[UPLOAD_FILE_TOOL] 📨 Received data event:`, JSON.stringify(event));
194
161
  if (event.intentName === "FileUploadForClaw") {
195
- logger.log(`[UPLOAD_FILE_TOOL] 🎯 FileUploadForClaw event received`);
196
- logger.log(`[UPLOAD_FILE_TOOL] - status: ${event.status}`);
197
162
  clearTimeout(timeout);
198
163
  wsManager.off("data-event", handler);
199
164
  if (event.status === "success" && event.outputs) {
200
- logger.log(`[UPLOAD_FILE_TOOL] ✅ File URL retrieval completed successfully`);
201
165
  // Check for error code in outputs
202
166
  const code = event.outputs.code !== undefined ? event.outputs.code : null;
203
167
  if (code !== null && code !== 0) {
204
- logger.error(`[UPLOAD_FILE_TOOL] ❌ Device returned error`);
205
- logger.error(`[UPLOAD_FILE_TOOL] - code: ${code}`);
206
168
  const errorMsg = event.outputs.errorMsg || event.outputs.errMsg || "未知错误";
207
- logger.error(`[UPLOAD_FILE_TOOL] - errorMsg: ${errorMsg}`);
208
169
  reject(new Error(`获取文件URL失败: ${errorMsg} (错误代码: ${code})`));
209
170
  return;
210
171
  }
@@ -222,29 +183,21 @@ async function getFileUrls(wsManager, config, sessionId, taskId, messageId, file
222
183
  // Replace \u003d with = and \u0026 with &
223
184
  fileUrls = fileUrls.map((url) => {
224
185
  if (typeof url !== 'string') {
225
- logger.warn(`[UPLOAD_FILE_TOOL] ⚠️ URL is not a string:`, typeof url);
226
186
  return '';
227
187
  }
228
188
  const decodedUrl = url
229
189
  .replace(/\\u003d/g, '=')
230
190
  .replace(/\\u0026/g, '&');
231
- logger.log(`[UPLOAD_FILE_TOOL] 🔄 Decoded URL: ${url} -> ${decodedUrl}`);
232
191
  return decodedUrl;
233
192
  }).filter((url) => url.length > 0);
234
- logger.log(`[UPLOAD_FILE_TOOL] 📊 Retrieved and decoded ${fileUrls.length} file URLs`);
235
193
  resolve(fileUrls);
236
194
  }
237
195
  else {
238
- logger.error(`[UPLOAD_FILE_TOOL] ❌ File URL retrieval failed`);
239
- logger.error(`[UPLOAD_FILE_TOOL] - status: ${event.status}`);
240
- logger.error(`[UPLOAD_FILE_TOOL] - outputs:`, JSON.stringify(event.outputs || {}));
241
196
  reject(new Error(`获取文件URL失败: ${event.status}`));
242
197
  }
243
198
  }
244
199
  };
245
- logger.log(`[UPLOAD_FILE_TOOL] 📡 Registering data-event handler for FileUploadForClaw`);
246
200
  wsManager.on("data-event", handler);
247
- logger.log(`[UPLOAD_FILE_TOOL] 📤 Sending FileUploadForClaw command...`);
248
201
  sendCommand({
249
202
  config,
250
203
  sessionId,
@@ -253,10 +206,8 @@ async function getFileUrls(wsManager, config, sessionId, taskId, messageId, file
253
206
  command,
254
207
  })
255
208
  .then(() => {
256
- logger.log(`[UPLOAD_FILE_TOOL] ✅ FileUploadForClaw command sent successfully`);
257
209
  })
258
210
  .catch((error) => {
259
- logger.error(`[UPLOAD_FILE_TOOL] ❌ Failed to send FileUploadForClaw command:`, error);
260
211
  clearTimeout(timeout);
261
212
  wsManager.off("data-event", handler);
262
213
  reject(error);
@@ -1,7 +1,6 @@
1
1
  import { getXYWebSocketManager } from "../client.js";
2
2
  import { sendCommand } from "../formatter.js";
3
3
  import { getCurrentSessionContext } from "./session-manager.js";
4
- import { logger } from "../utils/logger.js";
5
4
  /**
6
5
  * XY upload photo tool - uploads local photos to get publicly accessible URLs.
7
6
  * Requires mediaUris from search_photo_gallery tool as prerequisite.
@@ -36,79 +35,52 @@ export const uploadPhotoTool = {
36
35
  required: ["mediaUris"],
37
36
  },
38
37
  async execute(toolCallId, params) {
39
- logger.log(`[UPLOAD_PHOTO_TOOL] 🚀 Starting execution`);
40
- logger.log(`[UPLOAD_PHOTO_TOOL] - toolCallId: ${toolCallId}`);
41
- logger.log(`[UPLOAD_PHOTO_TOOL] - params (raw):`, JSON.stringify(params));
42
- logger.log(`[UPLOAD_PHOTO_TOOL] - params.mediaUris type:`, typeof params.mediaUris);
43
- logger.log(`[UPLOAD_PHOTO_TOOL] - timestamp: ${new Date().toISOString()}`);
44
38
  // ===== 参数规范化:兼容数组和 JSON 字符串 =====
45
39
  let mediaUris = null;
46
40
  if (!params.mediaUris) {
47
- logger.error(`[UPLOAD_PHOTO_TOOL] ❌ Missing parameter: mediaUris`);
48
41
  throw new Error("Missing required parameter: mediaUris");
49
42
  }
50
43
  // 情况1: 已经是数组
51
44
  if (Array.isArray(params.mediaUris)) {
52
- logger.log(`[UPLOAD_PHOTO_TOOL] ✅ mediaUris is already an array`);
53
45
  mediaUris = params.mediaUris;
54
46
  }
55
47
  // 情况2: 是字符串,尝试解析为 JSON 数组
56
48
  else if (typeof params.mediaUris === 'string') {
57
- logger.log(`[UPLOAD_PHOTO_TOOL] 🔄 mediaUris is a string, attempting to parse as JSON...`);
58
49
  try {
59
50
  const parsed = JSON.parse(params.mediaUris);
60
51
  if (Array.isArray(parsed)) {
61
- logger.log(`[UPLOAD_PHOTO_TOOL] ✅ Successfully parsed JSON string to array`);
62
52
  mediaUris = parsed;
63
53
  }
64
54
  else {
65
- logger.error(`[UPLOAD_PHOTO_TOOL] ❌ Parsed JSON is not an array:`, typeof parsed);
66
55
  throw new Error("mediaUris must be an array or a JSON string representing an array");
67
56
  }
68
57
  }
69
58
  catch (parseError) {
70
- logger.error(`[UPLOAD_PHOTO_TOOL] ❌ Failed to parse mediaUris as JSON:`, parseError);
71
59
  throw new Error(`mediaUris must be a valid JSON array string. Parse error: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
72
60
  }
73
61
  }
74
62
  // 情况3: 其他类型,报错
75
63
  else {
76
- logger.error(`[UPLOAD_PHOTO_TOOL] ❌ Invalid mediaUris type:`, typeof params.mediaUris);
77
64
  throw new Error(`mediaUris must be an array or a JSON string, got ${typeof params.mediaUris}`);
78
65
  }
79
66
  // 验证数组非空
80
67
  if (!mediaUris || mediaUris.length === 0) {
81
- logger.error(`[UPLOAD_PHOTO_TOOL] ❌ mediaUris array is empty`);
82
68
  throw new Error("mediaUris array cannot be empty");
83
69
  }
84
- logger.log(`[UPLOAD_PHOTO_TOOL] ✅ Normalized mediaUris:`, JSON.stringify(mediaUris));
85
70
  // Validate maximum 5 URIs
86
71
  if (mediaUris.length > 5) {
87
- logger.error(`[UPLOAD_PHOTO_TOOL] ❌ Too many mediaUris: ${mediaUris.length}`);
88
72
  throw new Error(`最多支持 5 条 mediaUri,当前提供了 ${mediaUris.length} 条。请分批处理。`);
89
73
  }
90
- logger.log(`[UPLOAD_PHOTO_TOOL] - mediaUris count: ${mediaUris.length}`);
91
74
  // Get session context
92
- logger.log(`[UPLOAD_PHOTO_TOOL] 🔍 Attempting to get session context...`);
93
75
  const sessionContext = getCurrentSessionContext();
94
76
  if (!sessionContext) {
95
- logger.error(`[UPLOAD_PHOTO_TOOL] ❌ FAILED: No active session found!`);
96
- logger.error(`[UPLOAD_PHOTO_TOOL] - toolCallId: ${toolCallId}`);
97
77
  throw new Error("No active XY session found. Upload photo tool can only be used during an active conversation.");
98
78
  }
99
- logger.log(`[UPLOAD_PHOTO_TOOL] ✅ Session context found`);
100
- logger.log(`[UPLOAD_PHOTO_TOOL] - sessionId: ${sessionContext.sessionId}`);
101
- logger.log(`[UPLOAD_PHOTO_TOOL] - taskId: ${sessionContext.taskId}`);
102
- logger.log(`[UPLOAD_PHOTO_TOOL] - messageId: ${sessionContext.messageId}`);
103
79
  const { config, sessionId, taskId, messageId } = sessionContext;
104
80
  // Get WebSocket manager
105
- logger.log(`[UPLOAD_PHOTO_TOOL] 🔌 Getting WebSocket manager...`);
106
81
  const wsManager = getXYWebSocketManager(config);
107
- logger.log(`[UPLOAD_PHOTO_TOOL] ✅ WebSocket manager obtained`);
108
82
  // Get public URLs for the photos
109
- logger.log(`[UPLOAD_PHOTO_TOOL] 🌐 Getting public URLs for photos...`);
110
83
  const imageUrls = await getPhotoUrls(wsManager, config, sessionId, taskId, messageId, mediaUris);
111
- logger.log(`[UPLOAD_PHOTO_TOOL] 🎉 Successfully retrieved ${imageUrls.length} photo URLs`);
112
84
  return {
113
85
  content: [
114
86
  {
@@ -128,7 +100,6 @@ export const uploadPhotoTool = {
128
100
  * Returns array of publicly accessible image URLs
129
101
  */
130
102
  async function getPhotoUrls(wsManager, config, sessionId, taskId, messageId, mediaUris) {
131
- logger.log(`[UPLOAD_PHOTO_TOOL] 📦 Building ImageUploadForClaw command...`);
132
103
  // Build imageInfos array from mediaUris
133
104
  const imageInfos = mediaUris.map(mediaUri => ({ mediaUri }));
134
105
  const command = {
@@ -166,19 +137,14 @@ async function getPhotoUrls(wsManager, config, sessionId, taskId, messageId, med
166
137
  };
167
138
  return new Promise((resolve, reject) => {
168
139
  const timeout = setTimeout(() => {
169
- logger.error(`[UPLOAD_PHOTO_TOOL] ⏰ Timeout: No response for ImageUploadForClaw within 60 seconds`);
170
140
  wsManager.off("data-event", handler);
171
141
  reject(new Error("获取照片URL超时(60秒)"));
172
142
  }, 60000);
173
143
  const handler = (event) => {
174
- logger.log(`[UPLOAD_PHOTO_TOOL] 📨 Received data event:`, JSON.stringify(event));
175
144
  if (event.intentName === "ImageUploadForClaw") {
176
- logger.log(`[UPLOAD_PHOTO_TOOL] 🎯 ImageUploadForClaw event received`);
177
- logger.log(`[UPLOAD_PHOTO_TOOL] - status: ${event.status}`);
178
145
  clearTimeout(timeout);
179
146
  wsManager.off("data-event", handler);
180
147
  if (event.status === "success" && event.outputs) {
181
- logger.log(`[UPLOAD_PHOTO_TOOL] ✅ Image URL retrieval completed successfully`);
182
148
  const result = event.outputs.result;
183
149
  let imageUrls = result?.imageUrls || [];
184
150
  // Decode Unicode escape sequences in URLs
@@ -187,22 +153,16 @@ async function getPhotoUrls(wsManager, config, sessionId, taskId, messageId, med
187
153
  const decodedUrl = url
188
154
  .replace(/\\u003d/g, '=')
189
155
  .replace(/\\u0026/g, '&');
190
- logger.log(`[UPLOAD_PHOTO_TOOL] 🔄 Decoded URL: ${url} -> ${decodedUrl}`);
191
156
  return decodedUrl;
192
157
  });
193
- logger.log(`[UPLOAD_PHOTO_TOOL] 📊 Retrieved and decoded ${imageUrls.length} image URLs`);
194
158
  resolve(imageUrls);
195
159
  }
196
160
  else {
197
- logger.error(`[UPLOAD_PHOTO_TOOL] ❌ Image URL retrieval failed`);
198
- logger.error(`[UPLOAD_PHOTO_TOOL] - status: ${event.status}`);
199
161
  reject(new Error(`获取照片URL失败: ${event.status}`));
200
162
  }
201
163
  }
202
164
  };
203
- logger.log(`[UPLOAD_PHOTO_TOOL] 📡 Registering data-event handler for ImageUploadForClaw`);
204
165
  wsManager.on("data-event", handler);
205
- logger.log(`[UPLOAD_PHOTO_TOOL] 📤 Sending ImageUploadForClaw command...`);
206
166
  sendCommand({
207
167
  config,
208
168
  sessionId,
@@ -211,10 +171,8 @@ async function getPhotoUrls(wsManager, config, sessionId, taskId, messageId, med
211
171
  command,
212
172
  })
213
173
  .then(() => {
214
- logger.log(`[UPLOAD_PHOTO_TOOL] ✅ ImageUploadForClaw command sent successfully`);
215
174
  })
216
175
  .catch((error) => {
217
- logger.error(`[UPLOAD_PHOTO_TOOL] ❌ Failed to send ImageUploadForClaw command:`, error);
218
176
  clearTimeout(timeout);
219
177
  wsManager.off("data-event", handler);
220
178
  reject(error);
@@ -1,5 +1,4 @@
1
1
  import { searchPushData, getAllPushData } from "../utils/pushdata-manager.js";
2
- import { logger } from "../utils/logger.js";
3
2
  /**
4
3
  * 查看推送任务执行结果工具
5
4
  * 支持关键词搜索或查看最近的推送记录
@@ -35,26 +34,18 @@ export const viewPushResultTool = {
35
34
  required: [],
36
35
  },
37
36
  async execute(toolCallId, params) {
38
- logger.log(`[VIEW_PUSH_RESULT_TOOL] 🚀 Starting execution`);
39
- logger.log(`[VIEW_PUSH_RESULT_TOOL] - toolCallId: ${toolCallId}`);
40
- logger.log(`[VIEW_PUSH_RESULT_TOOL] - params:`, JSON.stringify(params));
41
37
  const keywords = params.keywords?.trim();
42
38
  const limit = Math.min(params.limit || 10, 50); // 限制最多50条
43
39
  try {
44
- logger.log(`[VIEW_PUSH_RESULT_TOOL] 🔍 Searching push data...`);
45
- logger.log(`[VIEW_PUSH_RESULT_TOOL] - keywords: ${keywords || '(none)'}`);
46
- logger.log(`[VIEW_PUSH_RESULT_TOOL] - limit: ${limit}`);
47
40
  // 根据是否有关键词决定调用哪个方法
48
41
  let results = keywords
49
42
  ? await searchPushData(keywords)
50
43
  : await getAllPushData();
51
- logger.log(`[VIEW_PUSH_RESULT_TOOL] Found ${results.length} items before limit`);
52
44
  // 按时间倒序排序(最新的在前)
53
45
  results.sort((a, b) => b.time.localeCompare(a.time));
54
46
  // 限制返回条数
55
47
  results = results.slice(0, limit);
56
48
  if (results.length === 0) {
57
- logger.log(`[VIEW_PUSH_RESULT_TOOL] ⚠️ No results found`);
58
49
  return {
59
50
  content: [
60
51
  {
@@ -81,7 +72,6 @@ export const viewPushResultTool = {
81
72
  : item.dataDetail,
82
73
  fullLength: item.dataDetail.length,
83
74
  }));
84
- logger.log(`[VIEW_PUSH_RESULT_TOOL] ✅ Returning ${formattedItems.length} items`);
85
75
  return {
86
76
  content: [
87
77
  {
@@ -100,7 +90,6 @@ export const viewPushResultTool = {
100
90
  };
101
91
  }
102
92
  catch (error) {
103
- logger.error(`[VIEW_PUSH_RESULT_TOOL] ❌ Failed to execute:`, error);
104
93
  return {
105
94
  content: [
106
95
  {
@@ -1,7 +1,6 @@
1
1
  import { getXYWebSocketManager } from "../client.js";
2
2
  import { sendCommand } from "../formatter.js";
3
3
  import { getCurrentSessionContext } from "./session-manager.js";
4
- import { logger } from "../utils/logger.js";
5
4
  /**
6
5
  * XY collection tool - retrieves user's collection data from XiaoYi.
7
6
  * Returns personalized knowledge data saved in user's collection.
@@ -22,29 +21,15 @@ export const xiaoyiCollectionTool = {
22
21
  required: [],
23
22
  },
24
23
  async execute(toolCallId, params) {
25
- logger.log(`[XIAOYI_COLLECTION_TOOL] 🚀 Starting execution`);
26
- logger.log(`[XIAOYI_COLLECTION_TOOL] - toolCallId: ${toolCallId}`);
27
- logger.log(`[XIAOYI_COLLECTION_TOOL] - params:`, JSON.stringify(params));
28
- logger.log(`[XIAOYI_COLLECTION_TOOL] - timestamp: ${new Date().toISOString()}`);
29
24
  // Get session context
30
- logger.log(`[XIAOYI_COLLECTION_TOOL] 🔍 Attempting to get session context...`);
31
25
  const sessionContext = getCurrentSessionContext();
32
26
  if (!sessionContext) {
33
- logger.error(`[XIAOYI_COLLECTION_TOOL] ❌ FAILED: No active session found!`);
34
- logger.error(`[XIAOYI_COLLECTION_TOOL] - toolCallId: ${toolCallId}`);
35
27
  throw new Error("No active XY session found. XiaoYi collection tool can only be used during an active conversation.");
36
28
  }
37
- logger.log(`[XIAOYI_COLLECTION_TOOL] ✅ Session context found`);
38
- logger.log(`[XIAOYI_COLLECTION_TOOL] - sessionId: ${sessionContext.sessionId}`);
39
- logger.log(`[XIAOYI_COLLECTION_TOOL] - taskId: ${sessionContext.taskId}`);
40
- logger.log(`[XIAOYI_COLLECTION_TOOL] - messageId: ${sessionContext.messageId}`);
41
29
  const { config, sessionId, taskId, messageId } = sessionContext;
42
30
  // Get WebSocket manager
43
- logger.log(`[XIAOYI_COLLECTION_TOOL] 🔌 Getting WebSocket manager...`);
44
31
  const wsManager = getXYWebSocketManager(config);
45
- logger.log(`[XIAOYI_COLLECTION_TOOL] ✅ WebSocket manager obtained`);
46
32
  // Build QueryCollection command
47
- logger.log(`[XIAOYI_COLLECTION_TOOL] 📦 Building QueryCollection command...`);
48
33
  const command = {
49
34
  header: {
50
35
  namespace: "Common",
@@ -79,96 +64,35 @@ export const xiaoyiCollectionTool = {
79
64
  },
80
65
  };
81
66
  // Send command and wait for response (60 second timeout)
82
- logger.log(`[XIAOYI_COLLECTION_TOOL] ⏳ Setting up promise to wait for collection query response...`);
83
- logger.log(`[XIAOYI_COLLECTION_TOOL] - Timeout: 60 seconds`);
84
67
  return new Promise((resolve, reject) => {
85
68
  const timeout = setTimeout(() => {
86
- logger.error(`[XIAOYI_COLLECTION_TOOL] ⏰ Timeout: No response received within 60 seconds`);
87
69
  wsManager.off("data-event", handler);
88
70
  reject(new Error("查询小艺收藏超时(60秒)"));
89
71
  }, 60000);
90
72
  // Listen for data events from WebSocket
91
73
  const handler = (event) => {
92
- logger.log(`[XIAOYI_COLLECTION_TOOL] 📨 Received data event:`, JSON.stringify(event));
93
74
  if (event.intentName === "QueryCollection") {
94
- logger.log(`[XIAOYI_COLLECTION_TOOL] 🎯 QueryCollection event received`);
95
- logger.log(`[XIAOYI_COLLECTION_TOOL] - status: ${event.status}`);
96
75
  clearTimeout(timeout);
97
76
  wsManager.off("data-event", handler);
98
77
  if (event.status === "success" && event.outputs) {
99
- logger.log(`[XIAOYI_COLLECTION_TOOL] Collection query completed successfully`);
100
- logger.log(`[XIAOYI_COLLECTION_TOOL] - outputs:`, JSON.stringify(event.outputs));
101
- // Check for error code first
102
- if (event.outputs.code && event.outputs.code !== 0) {
103
- logger.error(`[XIAOYI_COLLECTION_TOOL] ❌ Query failed with error code: ${event.outputs.code}`);
104
- reject(new Error(`查询小艺收藏失败 (错误码: ${event.outputs.code})`));
105
- return;
106
- }
107
- // Get the result from outputs
108
- const result = event.outputs.result;
109
- // Check if result exists
110
- if (!result) {
111
- logger.warn(`[XIAOYI_COLLECTION_TOOL] ⚠️ No collection data found`);
112
- resolve({
113
- content: [
114
- {
115
- type: "text",
116
- text: JSON.stringify({
117
- success: true,
118
- memoryInfo: [],
119
- message: "未找到收藏数据"
120
- }),
121
- },
122
- ],
123
- });
124
- return;
125
- }
126
- // Extract memoryInfo from the nested result structure
127
- const memoryInfo = result.result?.memoryInfo || [];
128
- logger.log(`[XIAOYI_COLLECTION_TOOL] 📊 Collections found: ${memoryInfo.length} items`);
129
- // Process and simplify the collection data
130
- const simplifiedCollections = memoryInfo.map((item) => ({
131
- uuid: item.uuid,
132
- type: item.type,
133
- status: item.status,
134
- collectionTime: item.collectionTime,
135
- editTime: item.editTime,
136
- title: item.linkTitle || item.aiTitle || item.textTitle || item.imageTitle || item.podcastTitle || "",
137
- description: item.description || item.abstract || "",
138
- content: item.textContent || "",
139
- linkUrl: item.linkUrl,
140
- linkType: item.linkType,
141
- appName: item.appNameFromPab || item.appName || "",
142
- labels: item.label || [],
143
- collectionMethod: item.collectionMethod,
144
- }));
145
- // Return the result with valid string content
78
+ // 成功,直接返回完整的 event.outputs JSON 字符串
146
79
  resolve({
147
80
  content: [
148
81
  {
149
82
  type: "text",
150
- text: JSON.stringify({
151
- success: true,
152
- totalResults: simplifiedCollections.length,
153
- collections: simplifiedCollections,
154
- message: result.message,
155
- }),
156
- },
157
- ],
83
+ text: JSON.stringify(event.outputs),
84
+ }
85
+ ]
158
86
  });
159
87
  }
160
88
  else {
161
- logger.error(`[XIAOYI_COLLECTION_TOOL] ❌ Collection query failed`);
162
- logger.error(`[XIAOYI_COLLECTION_TOOL] - status: ${event.status}`);
163
89
  reject(new Error(`查询小艺收藏失败: ${event.status}`));
164
90
  }
165
91
  }
166
92
  };
167
93
  // Register event handler
168
- logger.log(`[XIAOYI_COLLECTION_TOOL] 📡 Registering data-event handler on WebSocket manager`);
169
94
  wsManager.on("data-event", handler);
170
95
  // Send the command
171
- logger.log(`[XIAOYI_COLLECTION_TOOL] 📤 Sending QueryCollection command...`);
172
96
  sendCommand({
173
97
  config,
174
98
  sessionId,
@@ -177,10 +101,8 @@ export const xiaoyiCollectionTool = {
177
101
  command,
178
102
  })
179
103
  .then(() => {
180
- logger.log(`[XIAOYI_COLLECTION_TOOL] ✅ Command sent successfully, waiting for response...`);
181
104
  })
182
105
  .catch((error) => {
183
- logger.error(`[XIAOYI_COLLECTION_TOOL] ❌ Failed to send command:`, error);
184
106
  clearTimeout(timeout);
185
107
  wsManager.off("data-event", handler);
186
108
  reject(error);