@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
  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 delete alarm tool - deletes existing alarms on user's device.
7
6
  * Requires entityId(s) from search_alarm or create_alarm tool as prerequisite.
@@ -27,7 +26,9 @@ export const deleteAlarmTool = {
27
26
  注意事项:
28
27
  1. 操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。
29
28
  2. 删除操作不可撤销,请确认 entityId 正确后再删除。
30
- 3. items 参数支持数组或 JSON 字符串格式,代码会自动解析。`,
29
+ 3. items 参数支持数组或 JSON 字符串格式,代码会自动解析。
30
+
31
+ 回复约束:如果工具返回没有授权或者其他报错,只需要完整描述没有授权或者其他报错内容即可,不需要主动给用户提供解决方案,例如告诉用户如何授权,如何解决报错等都是不需要的,请严格遵守。`,
31
32
  parameters: {
32
33
  type: "object",
33
34
  properties: {
@@ -40,85 +41,57 @@ export const deleteAlarmTool = {
40
41
  required: ["items"],
41
42
  },
42
43
  async execute(toolCallId, params) {
43
- logger.log(`[DELETE_ALARM_TOOL] 🚀 Starting execution`);
44
- logger.log(`[DELETE_ALARM_TOOL] - toolCallId: ${toolCallId}`);
45
- logger.log(`[DELETE_ALARM_TOOL] - params (raw):`, JSON.stringify(params));
46
- logger.log(`[DELETE_ALARM_TOOL] - params.items type:`, typeof params.items);
47
- logger.log(`[DELETE_ALARM_TOOL] - timestamp: ${new Date().toISOString()}`);
48
44
  // ===== 参数规范化:兼容数组和 JSON 字符串 =====
49
45
  let items = null;
50
46
  if (!params.items) {
51
- logger.error(`[DELETE_ALARM_TOOL] ❌ Missing parameter: items`);
52
47
  throw new Error("Missing required parameter: items");
53
48
  }
54
49
  // 情况1: 已经是数组
55
50
  if (Array.isArray(params.items)) {
56
- logger.log(`[DELETE_ALARM_TOOL] ✅ items is already an array`);
57
51
  items = params.items;
58
52
  }
59
53
  // 情况2: 是字符串,尝试解析为 JSON 数组
60
54
  else if (typeof params.items === 'string') {
61
- logger.log(`[DELETE_ALARM_TOOL] 🔄 items is a string, attempting to parse as JSON...`);
62
55
  try {
63
56
  const parsed = JSON.parse(params.items);
64
57
  if (Array.isArray(parsed)) {
65
- logger.log(`[DELETE_ALARM_TOOL] ✅ Successfully parsed JSON string to array`);
66
58
  items = parsed;
67
59
  }
68
60
  else {
69
- logger.error(`[DELETE_ALARM_TOOL] ❌ Parsed JSON is not an array:`, typeof parsed);
70
61
  throw new Error("items must be an array or a JSON string representing an array");
71
62
  }
72
63
  }
73
64
  catch (parseError) {
74
- logger.error(`[DELETE_ALARM_TOOL] ❌ Failed to parse items as JSON:`, parseError);
75
65
  throw new Error(`items must be a valid JSON array string. Parse error: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
76
66
  }
77
67
  }
78
68
  // 情况3: 其他类型,报错
79
69
  else {
80
- logger.error(`[DELETE_ALARM_TOOL] ❌ Invalid items type:`, typeof params.items);
81
70
  throw new Error(`items must be an array or a JSON string, got ${typeof params.items}`);
82
71
  }
83
72
  // 验证数组非空
84
73
  if (!items || items.length === 0) {
85
- logger.error(`[DELETE_ALARM_TOOL] ❌ items array is empty`);
86
74
  throw new Error("items array cannot be empty");
87
75
  }
88
- logger.log(`[DELETE_ALARM_TOOL] ✅ Normalized items:`, JSON.stringify(items));
89
76
  // 验证每个 item 是否有有效的 entityId
90
77
  for (let i = 0; i < items.length; i++) {
91
78
  const item = items[i];
92
79
  if (!item || typeof item !== 'object') {
93
- logger.error(`[DELETE_ALARM_TOOL] ❌ Item at index ${i} is not an object`);
94
80
  throw new Error(`items[${i}] must be an object with entityId property`);
95
81
  }
96
82
  if (!item.entityId || typeof item.entityId !== 'string') {
97
- logger.error(`[DELETE_ALARM_TOOL] ❌ Item at index ${i} missing or invalid entityId`);
98
83
  throw new Error(`items[${i}] must have a valid entityId string property`);
99
84
  }
100
85
  }
101
- logger.log(`[DELETE_ALARM_TOOL] - items count: ${items.length}`);
102
- logger.log(`[DELETE_ALARM_TOOL] - entityIds:`, items.map(item => item.entityId).join(", "));
103
86
  // Get session context
104
- logger.log(`[DELETE_ALARM_TOOL] 🔍 Attempting to get session context...`);
105
87
  const sessionContext = getCurrentSessionContext();
106
88
  if (!sessionContext) {
107
- logger.error(`[DELETE_ALARM_TOOL] ❌ FAILED: No active session found!`);
108
- logger.error(`[DELETE_ALARM_TOOL] - toolCallId: ${toolCallId}`);
109
89
  throw new Error("No active XY session found. Delete alarm tool can only be used during an active conversation.");
110
90
  }
111
- logger.log(`[DELETE_ALARM_TOOL] ✅ Session context found`);
112
- logger.log(`[DELETE_ALARM_TOOL] - sessionId: ${sessionContext.sessionId}`);
113
- logger.log(`[DELETE_ALARM_TOOL] - taskId: ${sessionContext.taskId}`);
114
- logger.log(`[DELETE_ALARM_TOOL] - messageId: ${sessionContext.messageId}`);
115
91
  const { config, sessionId, taskId, messageId } = sessionContext;
116
92
  // Get WebSocket manager
117
- logger.log(`[DELETE_ALARM_TOOL] 🔌 Getting WebSocket manager...`);
118
93
  const wsManager = getXYWebSocketManager(config);
119
- logger.log(`[DELETE_ALARM_TOOL] ✅ WebSocket manager obtained`);
120
94
  // Build DeleteAlarm command
121
- logger.log(`[DELETE_ALARM_TOOL] 📦 Building DeleteAlarm command...`);
122
95
  const command = {
123
96
  header: {
124
97
  namespace: "Common",
@@ -153,25 +126,17 @@ export const deleteAlarmTool = {
153
126
  },
154
127
  };
155
128
  // Send command and wait for response (60 second timeout)
156
- logger.log(`[DELETE_ALARM_TOOL] ⏳ Setting up promise to wait for alarm deletion response...`);
157
- logger.log(`[DELETE_ALARM_TOOL] - Timeout: 60 seconds`);
158
129
  return new Promise((resolve, reject) => {
159
130
  const timeout = setTimeout(() => {
160
- logger.error(`[DELETE_ALARM_TOOL] ⏰ Timeout: No response received within 60 seconds`);
161
131
  wsManager.off("data-event", handler);
162
132
  reject(new Error("删除闹钟超时(60秒)"));
163
133
  }, 60000);
164
134
  // Listen for data events from WebSocket
165
135
  const handler = (event) => {
166
- logger.log(`[DELETE_ALARM_TOOL] 📨 Received data event:`, JSON.stringify(event));
167
136
  if (event.intentName === "DeleteAlarm") {
168
- logger.log(`[DELETE_ALARM_TOOL] 🎯 DeleteAlarm event received`);
169
- logger.log(`[DELETE_ALARM_TOOL] - status: ${event.status}`);
170
137
  clearTimeout(timeout);
171
138
  wsManager.off("data-event", handler);
172
139
  if (event.status === "success" && event.outputs) {
173
- logger.log(`[DELETE_ALARM_TOOL] ✅ Alarm deletion completed successfully`);
174
- logger.log(`[DELETE_ALARM_TOOL] - outputs:`, JSON.stringify(event.outputs));
175
140
  // 成功,直接返回完整的 event.outputs JSON 字符串
176
141
  resolve({
177
142
  content: [
@@ -183,18 +148,13 @@ export const deleteAlarmTool = {
183
148
  });
184
149
  }
185
150
  else {
186
- logger.error(`[DELETE_ALARM_TOOL] ❌ Alarm deletion failed`);
187
- logger.error(`[DELETE_ALARM_TOOL] - status: ${event.status}`);
188
- logger.error(`[DELETE_ALARM_TOOL] - outputs:`, JSON.stringify(event.outputs || {}));
189
151
  reject(new Error(`删除闹钟失败: ${event.status}`));
190
152
  }
191
153
  }
192
154
  };
193
155
  // Register event handler
194
- logger.log(`[DELETE_ALARM_TOOL] 📡 Registering data-event handler on WebSocket manager`);
195
156
  wsManager.on("data-event", handler);
196
157
  // Send the command
197
- logger.log(`[DELETE_ALARM_TOOL] 📤 Sending DeleteAlarm command...`);
198
158
  sendCommand({
199
159
  config,
200
160
  sessionId,
@@ -203,10 +163,8 @@ export const deleteAlarmTool = {
203
163
  command,
204
164
  })
205
165
  .then(() => {
206
- logger.log(`[DELETE_ALARM_TOOL] ✅ Command sent successfully, waiting for response...`);
207
166
  })
208
167
  .catch((error) => {
209
- logger.error(`[DELETE_ALARM_TOOL] ❌ Failed to send command:`, error);
210
168
  clearTimeout(timeout);
211
169
  wsManager.off("data-event", handler);
212
170
  reject(error);
@@ -1,7 +1,6 @@
1
1
  // Image Reading tool implementation
2
2
  import { XYFileUploadService } from "../file-upload.js";
3
3
  import { getCurrentSessionContext } from "./session-manager.js";
4
- import { logger } from "../utils/logger.js";
5
4
  import fetch from "node-fetch";
6
5
  import fs from "fs/promises";
7
6
  import path from "path";
@@ -34,7 +33,6 @@ async function isLocalFile(value) {
34
33
  * Download remote file to local temp directory
35
34
  */
36
35
  async function downloadRemoteFile(url) {
37
- logger.log(`[IMAGE_READING_TOOL] 📥 Downloading remote file: ${url}`);
38
36
  try {
39
37
  const response = await fetch(url);
40
38
  if (!response.ok) {
@@ -56,11 +54,9 @@ async function downloadRemoteFile(url) {
56
54
  const arrayBuffer = await response.arrayBuffer();
57
55
  const buffer = Buffer.from(arrayBuffer);
58
56
  await fs.writeFile(localPath, buffer);
59
- logger.log(`[IMAGE_READING_TOOL] ✅ File downloaded to: ${localPath}`);
60
57
  return localPath;
61
58
  }
62
59
  catch (error) {
63
- logger.error(`[IMAGE_READING_TOOL] ❌ Failed to download file from ${url}:`, error);
64
60
  throw new Error(`Failed to download remote file: ${error instanceof Error ? error.message : String(error)}`);
65
61
  }
66
62
  }
@@ -68,30 +64,22 @@ async function downloadRemoteFile(url) {
68
64
  * Process image input: validate and convert local file to OBS URL, keep remote URL unchanged
69
65
  */
70
66
  async function processImageInput(imageInput, uploadService) {
71
- logger.log(`[IMAGE_READING_TOOL] 🔄 Processing image input: ${imageInput}`);
72
67
  // Check if it's a remote URL
73
68
  if (isRemoteUrl(imageInput)) {
74
- logger.log(`[IMAGE_READING_TOOL] 🌐 Input is remote URL, downloading...`);
75
69
  const localPath = await downloadRemoteFile(imageInput);
76
- logger.log(`[IMAGE_READING_TOOL] 📤 Uploading downloaded file to OBS...`);
77
70
  const imageUrl = await uploadService.uploadFileAndGetUrl(localPath, "TEMPORARY_MATERIAL_DOC");
78
71
  if (!imageUrl) {
79
- logger.error(`[IMAGE_READING_TOOL] ❌ Failed to get URL after upload`);
80
72
  throw new Error("图片上传失败:无法获取图片访问地址");
81
73
  }
82
- logger.log(`[IMAGE_READING_TOOL] ✅ Uploaded to OBS: ${imageUrl}`);
83
74
  return { imageUrl, localPath };
84
75
  }
85
76
  // Check if it's a local file
86
77
  const isLocal = await isLocalFile(imageInput);
87
78
  if (isLocal) {
88
- logger.log(`[IMAGE_READING_TOOL] 📁 Input is local file, uploading...`);
89
79
  const imageUrl = await uploadService.uploadFileAndGetUrl(imageInput, "TEMPORARY_MATERIAL_DOC");
90
80
  if (!imageUrl) {
91
- logger.error(`[IMAGE_READING_TOOL] ❌ Failed to get URL after upload`);
92
81
  throw new Error("图片上传失败:无法获取图片访问地址");
93
82
  }
94
- logger.log(`[IMAGE_READING_TOOL] ✅ Uploaded to OBS: ${imageUrl}`);
95
83
  return { imageUrl };
96
84
  }
97
85
  throw new Error(`Invalid image input: must be a remote URL or local file path, got: ${imageInput}`);
@@ -100,9 +88,6 @@ async function processImageInput(imageInput, uploadService) {
100
88
  * Call image understanding API with streaming response
101
89
  */
102
90
  async function callImageUnderstandingAPI(imageUrl, text, apiKey, uid) {
103
- logger.log(`[IMAGE_READING_TOOL] 🧠 Calling image understanding API...`);
104
- logger.log(`[IMAGE_READING_TOOL] - imageUrl: ${imageUrl}`);
105
- logger.log(`[IMAGE_READING_TOOL] - prompt: ${text}`);
106
91
  const apiUrl = "https://hag-drcn.op.dbankcloud.com/celia-claw/v1/sse-api/skill/execute";
107
92
  const traceId = uuidv4();
108
93
  const headers = {
@@ -149,7 +134,6 @@ async function callImageUnderstandingAPI(imageUrl, text, apiKey, uid) {
149
134
  },
150
135
  ],
151
136
  };
152
- logger.log(`[IMAGE_READING_TOOL] 📡 Sending request with trace ID: ${traceId}`);
153
137
  try {
154
138
  const response = await fetch(apiUrl, {
155
139
  method: "POST",
@@ -158,19 +142,14 @@ async function callImageUnderstandingAPI(imageUrl, text, apiKey, uid) {
158
142
  // @ts-ignore - node-fetch supports this
159
143
  timeout: 120000, // 2 minutes timeout
160
144
  });
161
- logger.log(`[IMAGE_READING_TOOL] 📨 Response status: ${response.status}`);
162
- logger.log(`[IMAGE_READING_TOOL] 📨 Content-Type: ${response.headers.get("Content-Type")}`);
163
145
  if (!response.ok) {
164
146
  const errorText = await response.text();
165
- logger.error(`[IMAGE_READING_TOOL] ❌ API request failed: ${response.status}`);
166
- logger.error(`[IMAGE_READING_TOOL] ❌ Response: ${errorText}`);
167
147
  throw new Error(`API request failed: ${response.status} ${response.statusText}`);
168
148
  }
169
149
  // Process SSE stream
170
150
  let lastCaption = "";
171
151
  let lineCount = 0;
172
152
  let buffer = "";
173
- logger.log(`[IMAGE_READING_TOOL] 📖 Reading SSE stream...`);
174
153
  // Read the response body as a stream
175
154
  if (!response.body) {
176
155
  throw new Error("Response body is null");
@@ -198,29 +177,23 @@ async function callImageUnderstandingAPI(imageUrl, text, apiKey, uid) {
198
177
  const streamContent = info.actionExecutorResult.reply.streamInfo.streamContent;
199
178
  if (streamContent) {
200
179
  lastCaption = streamContent;
201
- logger.log(`[IMAGE_READING_TOOL] 📝 Updated caption (length: ${streamContent.length})`);
202
180
  }
203
181
  }
204
182
  }
205
183
  }
206
184
  }
207
185
  catch (parseError) {
208
- logger.warn(`[IMAGE_READING_TOOL] ⚠️ Failed to parse JSON data:`, parseError);
209
186
  }
210
187
  }
211
188
  }
212
189
  }
213
190
  }
214
- logger.log(`[IMAGE_READING_TOOL] ✅ Stream processing complete`);
215
- logger.log(`[IMAGE_READING_TOOL] - Total lines processed: ${lineCount}`);
216
- logger.log(`[IMAGE_READING_TOOL] - Final caption length: ${lastCaption.length}`);
217
191
  if (!lastCaption) {
218
192
  throw new Error("No caption received from image understanding API");
219
193
  }
220
194
  return lastCaption;
221
195
  }
222
196
  catch (error) {
223
- logger.error(`[IMAGE_READING_TOOL] ❌ API call failed:`, error);
224
197
  throw error;
225
198
  }
226
199
  }
@@ -278,26 +251,17 @@ d. 返回图像理解的文本描述内容`,
278
251
  },
279
252
  },
280
253
  async execute(toolCallId, params) {
281
- logger.log(`[IMAGE_READING_TOOL] 🚀 Starting execution`);
282
- logger.log(`[IMAGE_READING_TOOL] - toolCallId: ${toolCallId}`);
283
- logger.log(`[IMAGE_READING_TOOL] - params:`, JSON.stringify(params));
284
- logger.log(`[IMAGE_READING_TOOL] - timestamp: ${new Date().toISOString()}`);
285
254
  // Validate that at least one parameter is provided
286
255
  if (!params.localUrl && !params.remoteUrl) {
287
- logger.error(`[IMAGE_READING_TOOL] ❌ Missing both localUrl and remoteUrl parameters`);
288
256
  throw new Error("At least one of localUrl or remoteUrl must be provided");
289
257
  }
290
258
  // Get prompt (default to "描述这张图片内容")
291
259
  const prompt = params.prompt || "描述这张图片内容";
292
- logger.log(`[IMAGE_READING_TOOL] 📝 Using prompt: ${prompt}`);
293
260
  // Get session context
294
- logger.log(`[IMAGE_READING_TOOL] 🔍 Getting session context...`);
295
261
  const sessionContext = getCurrentSessionContext();
296
262
  if (!sessionContext) {
297
- logger.error(`[IMAGE_READING_TOOL] ❌ No active session found!`);
298
263
  throw new Error("No active XY session found. Image reading tool can only be used during an active conversation.");
299
264
  }
300
- logger.log(`[IMAGE_READING_TOOL] ✅ Session context found`);
301
265
  const { config } = sessionContext;
302
266
  // Create upload service
303
267
  const uploadService = new XYFileUploadService(config.fileUploadUrl, config.apiKey, config.uid);
@@ -306,27 +270,19 @@ d. 返回图像理解的文本描述内容`,
306
270
  try {
307
271
  // Process image input (prefer localUrl over remoteUrl)
308
272
  const imageInput = params.localUrl || params.remoteUrl;
309
- logger.log(`[IMAGE_READING_TOOL] 🖼️ Processing image: ${imageInput}`);
310
273
  processedImage = await processImageInput(imageInput, uploadService);
311
274
  // Track downloaded file for cleanup
312
275
  if (processedImage.localPath) {
313
276
  downloadedFile = processedImage.localPath;
314
277
  }
315
- logger.log(`[IMAGE_READING_TOOL] ✅ Image processed successfully`);
316
- logger.log(`[IMAGE_READING_TOOL] - OBS URL: ${processedImage.imageUrl}`);
317
278
  // Call image understanding API
318
279
  const caption = await callImageUnderstandingAPI(processedImage.imageUrl, prompt, config.apiKey, config.uid);
319
- logger.log(`[IMAGE_READING_TOOL] 🎉 Image understanding completed successfully`);
320
- logger.log(`[IMAGE_READING_TOOL] - Caption length: ${caption.length} characters`);
321
280
  // Clean up downloaded file if any
322
281
  if (downloadedFile) {
323
- logger.log(`[IMAGE_READING_TOOL] 🧹 Cleaning up downloaded file...`);
324
282
  try {
325
283
  await fs.unlink(downloadedFile);
326
- logger.log(`[IMAGE_READING_TOOL] ✅ Cleaned up: ${downloadedFile}`);
327
284
  }
328
285
  catch (error) {
329
- logger.warn(`[IMAGE_READING_TOOL] ⚠️ Failed to clean up file:`, error);
330
286
  }
331
287
  }
332
288
  return {
@@ -346,15 +302,12 @@ d. 返回图像理解的文本描述内容`,
346
302
  catch (error) {
347
303
  // Clean up downloaded file on error
348
304
  if (downloadedFile) {
349
- logger.log(`[IMAGE_READING_TOOL] 🧹 Cleaning up downloaded file after error...`);
350
305
  try {
351
306
  await fs.unlink(downloadedFile);
352
307
  }
353
308
  catch (cleanupError) {
354
- logger.warn(`[IMAGE_READING_TOOL] ⚠️ Failed to clean up file:`, cleanupError);
355
309
  }
356
310
  }
357
- logger.error(`[IMAGE_READING_TOOL] ❌ Execution failed:`, error);
358
311
  const errorMessage = error instanceof Error ? error.message : "图片分析失败";
359
312
  // Return error result instead of throwing
360
313
  return {
@@ -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 location tool - gets user's current location.
7
6
  * Returns WGS84 coordinates (latitude, longitude).
@@ -9,38 +8,22 @@ import { logger } from "../utils/logger.js";
9
8
  export const locationTool = {
10
9
  name: "get_user_location",
11
10
  label: "Get User Location",
12
- description: "获取用户当前位置(经纬度坐标,WGS84坐标系)。需要用户设备授权位置访问权限。注意:操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。",
11
+ description: "获取用户当前位置(经纬度坐标,WGS84坐标系)。需要用户设备授权位置访问权限。注意:操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。回复约束:如果工具返回没有授权或者其他报错,只需要完整描述没有授权或者其他报错内容即可,不需要主动给用户提供解决方案,例如告诉用户如何授权,如何解决报错等都是不需要的,请严格遵守。",
13
12
  parameters: {
14
13
  type: "object",
15
14
  properties: {},
16
15
  required: [],
17
16
  },
18
17
  async execute(toolCallId, params) {
19
- logger.log(`[LOCATION_TOOL] 🚀 Starting execution`);
20
- logger.log(`[LOCATION_TOOL] - toolCallId: ${toolCallId}`);
21
- logger.log(`[LOCATION_TOOL] - params:`, JSON.stringify(params));
22
- logger.log(`[LOCATION_TOOL] - timestamp: ${new Date().toISOString()}`);
23
18
  // Get session context
24
- logger.log(`[LOCATION_TOOL] 🔍 Attempting to get session context...`);
25
19
  const sessionContext = getCurrentSessionContext();
26
20
  if (!sessionContext) {
27
- logger.error(`[LOCATION_TOOL] ❌ FAILED: No active session found!`);
28
- logger.error(`[LOCATION_TOOL] - toolCallId: ${toolCallId}`);
29
- logger.error(`[LOCATION_TOOL] - This suggests the session was not registered or already cleaned up`);
30
21
  throw new Error("No active XY session found. Location tool can only be used during an active conversation.");
31
22
  }
32
- logger.log(`[LOCATION_TOOL] ✅ Session context found`);
33
- logger.log(`[LOCATION_TOOL] - sessionId: ${sessionContext.sessionId}`);
34
- logger.log(`[LOCATION_TOOL] - taskId: ${sessionContext.taskId}`);
35
- logger.log(`[LOCATION_TOOL] - messageId: ${sessionContext.messageId}`);
36
- logger.log(`[LOCATION_TOOL] - agentId: ${sessionContext.agentId}`);
37
23
  const { config, sessionId, taskId, messageId } = sessionContext;
38
24
  // Get WebSocket manager
39
- logger.log(`[LOCATION_TOOL] 🔌 Getting WebSocket manager...`);
40
25
  const wsManager = getXYWebSocketManager(config);
41
- logger.log(`[LOCATION_TOOL] ✅ WebSocket manager obtained`);
42
26
  // Build GetCurrentLocation command
43
- logger.log(`[LOCATION_TOOL] 📦 Building GetCurrentLocation command...`);
44
27
  const command = {
45
28
  header: {
46
29
  namespace: "Common",
@@ -72,25 +55,17 @@ export const locationTool = {
72
55
  },
73
56
  };
74
57
  // Send command and wait for response (60 second timeout)
75
- logger.log(`[LOCATION_TOOL] ⏳ Setting up promise to wait for location response...`);
76
- logger.log(`[LOCATION_TOOL] - Timeout: 60 seconds`);
77
58
  return new Promise((resolve, reject) => {
78
59
  const timeout = setTimeout(() => {
79
- logger.error(`[LOCATION_TOOL] ⏰ Timeout: No response received within 60 seconds`);
80
60
  wsManager.off("data-event", handler);
81
61
  reject(new Error("获取位置超时(60秒)"));
82
62
  }, 60000);
83
63
  // Listen for data events from WebSocket
84
64
  const handler = (event) => {
85
- logger.log(`[LOCATION_TOOL] 📨 Received data event:`, JSON.stringify(event));
86
65
  if (event.intentName === "GetCurrentLocation") {
87
- logger.log(`[LOCATION_TOOL] 🎯 GetCurrentLocation event received`);
88
- logger.log(`[LOCATION_TOOL] - status: ${event.status}`);
89
66
  clearTimeout(timeout);
90
67
  wsManager.off("data-event", handler);
91
68
  if (event.status === "success" && event.outputs) {
92
- logger.log(`[LOCATION_TOOL] ✅ Location retrieved successfully`);
93
- logger.log(`[LOCATION_TOOL] - outputs:`, JSON.stringify(event.outputs));
94
69
  // 成功,直接返回完整的 event.outputs JSON 字符串
95
70
  resolve({
96
71
  content: [
@@ -102,18 +77,14 @@ export const locationTool = {
102
77
  });
103
78
  }
104
79
  else {
105
- logger.error(`[LOCATION_TOOL] ❌ Location retrieval failed`);
106
- logger.error(`[LOCATION_TOOL] - status: ${event.status}`);
107
80
  reject(new Error(`获取位置失败: ${event.status}`));
108
81
  }
109
82
  }
110
83
  };
111
84
  // Register event handler
112
85
  // Note: The WebSocket manager needs to emit 'data-event' when receiving data events
113
- logger.log(`[LOCATION_TOOL] 📡 Registering data-event handler on WebSocket manager`);
114
86
  wsManager.on("data-event", handler);
115
87
  // Send the command
116
- logger.log(`[LOCATION_TOOL] 📤 Sending GetCurrentLocation command...`);
117
88
  sendCommand({
118
89
  config,
119
90
  sessionId,
@@ -121,9 +92,7 @@ export const locationTool = {
121
92
  messageId,
122
93
  command,
123
94
  }).then(() => {
124
- logger.log(`[LOCATION_TOOL] ✅ Command sent successfully, waiting for response...`);
125
95
  }).catch((error) => {
126
- logger.error(`[LOCATION_TOOL] ❌ Failed to send command:`, error);
127
96
  clearTimeout(timeout);
128
97
  wsManager.off("data-event", handler);
129
98
  reject(error);