@ynhcj/xiaoyi-channel 0.0.44-beta → 0.0.44-next

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 (73) hide show
  1. package/dist/index.d.ts +0 -2
  2. package/dist/index.js +42 -2
  3. package/dist/src/bot.d.ts +1 -0
  4. package/dist/src/bot.js +59 -61
  5. package/dist/src/channel.js +30 -4
  6. package/dist/src/client.js +0 -9
  7. package/dist/src/cspl/call-api.d.ts +3 -0
  8. package/dist/src/cspl/call-api.js +86 -0
  9. package/dist/src/cspl/config.d.ts +19 -0
  10. package/dist/src/cspl/config.js +50 -0
  11. package/dist/src/cspl/constants.d.ts +43 -0
  12. package/dist/src/cspl/constants.js +22 -0
  13. package/dist/src/cspl/utils.d.ts +10 -0
  14. package/dist/src/cspl/utils.js +57 -0
  15. package/dist/src/file-upload.d.ts +5 -0
  16. package/dist/src/file-upload.js +88 -6
  17. package/dist/src/formatter.d.ts +2 -0
  18. package/dist/src/formatter.js +13 -30
  19. package/dist/src/heartbeat.js +0 -4
  20. package/dist/src/monitor.js +8 -10
  21. package/dist/src/onboarding.d.ts +3 -4
  22. package/dist/src/onboarding.js +2 -2
  23. package/dist/src/outbound.d.ts +2 -1
  24. package/dist/src/outbound.js +1 -19
  25. package/dist/src/parser.d.ts +6 -0
  26. package/dist/src/parser.js +16 -0
  27. package/dist/src/push.js +0 -21
  28. package/dist/src/reply-dispatcher.d.ts +4 -0
  29. package/dist/src/reply-dispatcher.js +46 -8
  30. package/dist/src/steer-injector.d.ts +16 -0
  31. package/dist/src/steer-injector.js +74 -0
  32. package/dist/src/thread-bindings.d.ts +54 -0
  33. package/dist/src/thread-bindings.js +214 -0
  34. package/dist/src/tools/calendar-tool.js +2 -37
  35. package/dist/src/tools/call-phone-tool.js +3 -60
  36. package/dist/src/tools/create-alarm-tool.js +8 -109
  37. package/dist/src/tools/delete-alarm-tool.js +5 -69
  38. package/dist/src/tools/device-tool-map.d.ts +4 -0
  39. package/dist/src/tools/device-tool-map.js +24 -0
  40. package/dist/src/tools/image-reading-tool.d.ts +5 -0
  41. package/dist/src/tools/image-reading-tool.js +328 -0
  42. package/dist/src/tools/location-tool.js +6 -40
  43. package/dist/src/tools/modify-alarm-tool.js +8 -114
  44. package/dist/src/tools/modify-note-tool.js +3 -41
  45. package/dist/src/tools/note-tool.js +4 -16
  46. package/dist/src/tools/search-alarm-tool.js +12 -118
  47. package/dist/src/tools/search-calendar-tool.js +4 -81
  48. package/dist/src/tools/search-contact-tool.js +2 -55
  49. package/dist/src/tools/search-file-tool.js +4 -61
  50. package/dist/src/tools/search-message-tool.js +2 -59
  51. package/dist/src/tools/search-note-tool.js +4 -22
  52. package/dist/src/tools/search-photo-gallery-tool.js +8 -57
  53. package/dist/src/tools/send-command-to-car-tool.d.ts +5 -0
  54. package/dist/src/tools/send-command-to-car-tool.js +85 -0
  55. package/dist/src/tools/send-file-to-user-tool.js +1 -39
  56. package/dist/src/tools/send-message-tool.js +5 -56
  57. package/dist/src/tools/session-manager.d.ts +1 -0
  58. package/dist/src/tools/session-manager.js +0 -45
  59. package/dist/src/tools/timestamp-to-utc8-tool.d.ts +12 -0
  60. package/dist/src/tools/timestamp-to-utc8-tool.js +104 -0
  61. package/dist/src/tools/upload-file-tool.js +0 -49
  62. package/dist/src/tools/upload-photo-tool.js +0 -42
  63. package/dist/src/tools/view-push-result-tool.js +0 -11
  64. package/dist/src/tools/xiaoyi-collection-tool.js +4 -82
  65. package/dist/src/tools/xiaoyi-gui-tool.js +0 -34
  66. package/dist/src/utils/runtime-manager.d.ts +7 -0
  67. package/dist/src/utils/runtime-manager.js +42 -0
  68. package/dist/src/websocket.js +33 -13
  69. package/package.json +3 -4
  70. package/dist/src/tools/search-photo-tool.d.ts +0 -9
  71. package/dist/src/tools/search-photo-tool.js +0 -270
  72. package/dist/src/utils/session.d.ts +0 -34
  73. package/dist/src/utils/session.js +0 -50
@@ -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 search file tool - searches files on user's device file system.
7
6
  * Returns matching files based on keyword search in file name or content.
@@ -33,35 +32,19 @@ export const searchFileTool = {
33
32
  required: ["query"],
34
33
  },
35
34
  async execute(toolCallId, params) {
36
- logger.log(`[SEARCH_FILE_TOOL] 🚀 Starting execution`);
37
- logger.log(`[SEARCH_FILE_TOOL] - toolCallId: ${toolCallId}`);
38
- logger.log(`[SEARCH_FILE_TOOL] - params:`, JSON.stringify(params));
39
- logger.log(`[SEARCH_FILE_TOOL] - timestamp: ${new Date().toISOString()}`);
40
35
  // Validate query parameter
41
36
  if (!params.query || typeof params.query !== "string" || params.query.trim() === "") {
42
- logger.error(`[SEARCH_FILE_TOOL] ❌ Missing or invalid query parameter`);
43
37
  throw new Error("Missing required parameter: query must be a non-empty string");
44
38
  }
45
- logger.log(`[SEARCH_FILE_TOOL] 🔍 Searching for files with keyword: ${params.query}`);
46
39
  // Get session context
47
- logger.log(`[SEARCH_FILE_TOOL] 🔍 Attempting to get session context...`);
48
40
  const sessionContext = getCurrentSessionContext();
49
41
  if (!sessionContext) {
50
- logger.error(`[SEARCH_FILE_TOOL] ❌ FAILED: No active session found!`);
51
- logger.error(`[SEARCH_FILE_TOOL] - toolCallId: ${toolCallId}`);
52
42
  throw new Error("No active XY session found. Search file tool can only be used during an active conversation.");
53
43
  }
54
- logger.log(`[SEARCH_FILE_TOOL] ✅ Session context found`);
55
- logger.log(`[SEARCH_FILE_TOOL] - sessionId: ${sessionContext.sessionId}`);
56
- logger.log(`[SEARCH_FILE_TOOL] - taskId: ${sessionContext.taskId}`);
57
- logger.log(`[SEARCH_FILE_TOOL] - messageId: ${sessionContext.messageId}`);
58
44
  const { config, sessionId, taskId, messageId } = sessionContext;
59
45
  // Get WebSocket manager
60
- logger.log(`[SEARCH_FILE_TOOL] 🔌 Getting WebSocket manager...`);
61
46
  const wsManager = getXYWebSocketManager(config);
62
- logger.log(`[SEARCH_FILE_TOOL] ✅ WebSocket manager obtained`);
63
47
  // Build SearchFile command
64
- logger.log(`[SEARCH_FILE_TOOL] 📦 Building SearchFile command...`);
65
48
  const command = {
66
49
  header: {
67
50
  namespace: "Common",
@@ -95,75 +78,37 @@ export const searchFileTool = {
95
78
  pageControlRelated: false,
96
79
  },
97
80
  };
98
- logger.log(`[SEARCH_FILE_TOOL] 📋 Command details:`, JSON.stringify(command, null, 2));
99
81
  // Send command and wait for response (60 second timeout)
100
- logger.log(`[SEARCH_FILE_TOOL] ⏳ Setting up promise to wait for file search response...`);
101
- logger.log(`[SEARCH_FILE_TOOL] - Timeout: 60 seconds`);
102
82
  return new Promise((resolve, reject) => {
103
83
  const timeout = setTimeout(() => {
104
- logger.error(`[SEARCH_FILE_TOOL] ⏰ Timeout: No response received within 60 seconds`);
105
84
  wsManager.off("data-event", handler);
106
85
  reject(new Error("搜索文件超时(60秒)"));
107
86
  }, 60000);
108
87
  // Listen for data events from WebSocket
109
88
  const handler = (event) => {
110
- logger.log(`[SEARCH_FILE_TOOL] 📨 Received data event:`, JSON.stringify(event));
111
89
  if (event.intentName === "SearchFile") {
112
- logger.log(`[SEARCH_FILE_TOOL] 🎯 SearchFile event received`);
113
- logger.log(`[SEARCH_FILE_TOOL] - status: ${event.status}`);
114
90
  clearTimeout(timeout);
115
91
  wsManager.off("data-event", handler);
116
92
  if (event.status === "success" && event.outputs) {
117
- logger.log(`[SEARCH_FILE_TOOL] File search response received`);
118
- logger.log(`[SEARCH_FILE_TOOL] - outputs:`, JSON.stringify(event.outputs));
119
- // Check for error code in outputs
120
- const code = event.outputs.code !== undefined ? event.outputs.code : null;
121
- if (code !== null && code !== 0) {
122
- logger.error(`[SEARCH_FILE_TOOL] ❌ Device returned error`);
123
- logger.error(`[SEARCH_FILE_TOOL] - code: ${code}`);
124
- const errorMsg = event.outputs.errorMsg || event.outputs.errMsg || "未知错误";
125
- logger.error(`[SEARCH_FILE_TOOL] - errorMsg: ${errorMsg}`);
126
- reject(new Error(`搜索文件失败: ${errorMsg} (错误代码: ${code})`));
127
- return;
128
- }
129
- // Extract result.items with safe checks
130
- const result = event.outputs.result;
131
- let items = [];
132
- if (result && typeof result === "object" && Array.isArray(result.items)) {
133
- items = result.items;
134
- logger.log(`[SEARCH_FILE_TOOL] 📋 Found ${items.length} file(s)`);
135
- }
136
- else {
137
- logger.warn(`[SEARCH_FILE_TOOL] ⚠️ No items found in result or result is invalid`);
138
- logger.warn(`[SEARCH_FILE_TOOL] - result:`, JSON.stringify(result || {}));
139
- }
140
- // Return items array as JSON string
141
- logger.log(`[SEARCH_FILE_TOOL] 🎉 File search completed successfully`);
142
- logger.log(`[SEARCH_FILE_TOOL] - keyword: ${params.query}`);
143
- logger.log(`[SEARCH_FILE_TOOL] - result count: ${items.length}`);
93
+ // 成功,直接返回完整的 event.outputs JSON 字符串
144
94
  resolve({
145
95
  content: [
146
96
  {
147
97
  type: "text",
148
- text: JSON.stringify(items),
149
- },
150
- ],
98
+ text: JSON.stringify(event.outputs),
99
+ }
100
+ ]
151
101
  });
152
102
  }
153
103
  else {
154
- logger.error(`[SEARCH_FILE_TOOL] ❌ File search failed`);
155
- logger.error(`[SEARCH_FILE_TOOL] - status: ${event.status}`);
156
- logger.error(`[SEARCH_FILE_TOOL] - outputs:`, JSON.stringify(event.outputs || {}));
157
104
  const errorDetail = event.outputs ? JSON.stringify(event.outputs) : event.status;
158
105
  reject(new Error(`搜索文件失败: ${errorDetail}`));
159
106
  }
160
107
  }
161
108
  };
162
109
  // Register event handler
163
- logger.log(`[SEARCH_FILE_TOOL] 📡 Registering data-event handler on WebSocket manager`);
164
110
  wsManager.on("data-event", handler);
165
111
  // Send the command
166
- logger.log(`[SEARCH_FILE_TOOL] 📤 Sending SearchFile command...`);
167
112
  sendCommand({
168
113
  config,
169
114
  sessionId,
@@ -172,10 +117,8 @@ export const searchFileTool = {
172
117
  command,
173
118
  })
174
119
  .then(() => {
175
- logger.log(`[SEARCH_FILE_TOOL] ✅ Command sent successfully, waiting for response...`);
176
120
  })
177
121
  .catch((error) => {
178
- logger.error(`[SEARCH_FILE_TOOL] ❌ Failed to send command:`, error);
179
122
  clearTimeout(timeout);
180
123
  wsManager.off("data-event", handler);
181
124
  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 search message tool - searches SMS messages on user's device.
7
6
  * Returns matching messages based on content keyword search.
@@ -21,35 +20,19 @@ export const searchMessageTool = {
21
20
  required: ["content"],
22
21
  },
23
22
  async execute(toolCallId, params) {
24
- logger.log(`[SEARCH_MESSAGE_TOOL] 🚀 Starting execution`);
25
- logger.log(`[SEARCH_MESSAGE_TOOL] - toolCallId: ${toolCallId}`);
26
- logger.log(`[SEARCH_MESSAGE_TOOL] - params:`, JSON.stringify(params));
27
- logger.log(`[SEARCH_MESSAGE_TOOL] - timestamp: ${new Date().toISOString()}`);
28
23
  // Validate content parameter
29
24
  if (!params.content || typeof params.content !== "string" || params.content.trim() === "") {
30
- logger.error(`[SEARCH_MESSAGE_TOOL] ❌ Missing or invalid content parameter`);
31
25
  throw new Error("Missing required parameter: content must be a non-empty string");
32
26
  }
33
- logger.log(`[SEARCH_MESSAGE_TOOL] 🔍 Searching for messages with keyword: ${params.content}`);
34
27
  // Get session context
35
- logger.log(`[SEARCH_MESSAGE_TOOL] 🔍 Attempting to get session context...`);
36
28
  const sessionContext = getCurrentSessionContext();
37
29
  if (!sessionContext) {
38
- logger.error(`[SEARCH_MESSAGE_TOOL] ❌ FAILED: No active session found!`);
39
- logger.error(`[SEARCH_MESSAGE_TOOL] - toolCallId: ${toolCallId}`);
40
30
  throw new Error("No active XY session found. Search message tool can only be used during an active conversation.");
41
31
  }
42
- logger.log(`[SEARCH_MESSAGE_TOOL] ✅ Session context found`);
43
- logger.log(`[SEARCH_MESSAGE_TOOL] - sessionId: ${sessionContext.sessionId}`);
44
- logger.log(`[SEARCH_MESSAGE_TOOL] - taskId: ${sessionContext.taskId}`);
45
- logger.log(`[SEARCH_MESSAGE_TOOL] - messageId: ${sessionContext.messageId}`);
46
32
  const { config, sessionId, taskId, messageId } = sessionContext;
47
33
  // Get WebSocket manager
48
- logger.log(`[SEARCH_MESSAGE_TOOL] 🔌 Getting WebSocket manager...`);
49
34
  const wsManager = getXYWebSocketManager(config);
50
- logger.log(`[SEARCH_MESSAGE_TOOL] ✅ WebSocket manager obtained`);
51
35
  // Build SearchMessage command
52
- logger.log(`[SEARCH_MESSAGE_TOOL] 📦 Building SearchMessage command...`);
53
36
  const command = {
54
37
  header: {
55
38
  namespace: "Common",
@@ -83,75 +66,37 @@ export const searchMessageTool = {
83
66
  pageControlRelated: false,
84
67
  },
85
68
  };
86
- logger.log(`[SEARCH_MESSAGE_TOOL] 📋 Command details:`, JSON.stringify(command, null, 2));
87
69
  // Send command and wait for response (60 second timeout)
88
- logger.log(`[SEARCH_MESSAGE_TOOL] ⏳ Setting up promise to wait for message search response...`);
89
- logger.log(`[SEARCH_MESSAGE_TOOL] - Timeout: 60 seconds`);
90
70
  return new Promise((resolve, reject) => {
91
71
  const timeout = setTimeout(() => {
92
- logger.error(`[SEARCH_MESSAGE_TOOL] ⏰ Timeout: No response received within 60 seconds`);
93
72
  wsManager.off("data-event", handler);
94
73
  reject(new Error("搜索短信超时(60秒)"));
95
74
  }, 60000);
96
75
  // Listen for data events from WebSocket
97
76
  const handler = (event) => {
98
- logger.log(`[SEARCH_MESSAGE_TOOL] 📨 Received data event:`, JSON.stringify(event));
99
77
  if (event.intentName === "SearchMessage") {
100
- logger.log(`[SEARCH_MESSAGE_TOOL] 🎯 SearchMessage event received`);
101
- logger.log(`[SEARCH_MESSAGE_TOOL] - status: ${event.status}`);
102
78
  clearTimeout(timeout);
103
79
  wsManager.off("data-event", handler);
104
80
  if (event.status === "success" && event.outputs) {
105
- logger.log(`[SEARCH_MESSAGE_TOOL] Message search response received`);
106
- logger.log(`[SEARCH_MESSAGE_TOOL] - outputs:`, JSON.stringify(event.outputs));
107
- // Check for error code in outputs
108
- const code = event.outputs.code !== undefined ? event.outputs.code : null;
109
- if (code !== null && code !== 0) {
110
- logger.error(`[SEARCH_MESSAGE_TOOL] ❌ Device returned error`);
111
- logger.error(`[SEARCH_MESSAGE_TOOL] - code: ${code}`);
112
- const errorMsg = event.outputs.errorMsg || event.outputs.errMsg || "未知错误";
113
- logger.error(`[SEARCH_MESSAGE_TOOL] - errorMsg: ${errorMsg}`);
114
- reject(new Error(`搜索短信失败: ${errorMsg} (错误代码: ${code})`));
115
- return;
116
- }
117
- // Extract result.items with safe checks
118
- const result = event.outputs.result;
119
- let items = [];
120
- if (result && typeof result === "object" && Array.isArray(result.items)) {
121
- items = result.items;
122
- logger.log(`[SEARCH_MESSAGE_TOOL] 📋 Found ${items.length} message(s)`);
123
- }
124
- else {
125
- logger.warn(`[SEARCH_MESSAGE_TOOL] ⚠️ No items found in result or result is invalid`);
126
- logger.warn(`[SEARCH_MESSAGE_TOOL] - result:`, JSON.stringify(result || {}));
127
- }
128
- // Return items array as JSON string
129
- logger.log(`[SEARCH_MESSAGE_TOOL] 🎉 Message search completed successfully`);
130
- logger.log(`[SEARCH_MESSAGE_TOOL] - keyword: ${params.content}`);
131
- logger.log(`[SEARCH_MESSAGE_TOOL] - result count: ${items.length}`);
81
+ // 成功,直接返回完整的 event.outputs JSON 字符串
132
82
  resolve({
133
83
  content: [
134
84
  {
135
85
  type: "text",
136
- text: JSON.stringify(items),
86
+ text: JSON.stringify(event.outputs),
137
87
  },
138
88
  ],
139
89
  });
140
90
  }
141
91
  else {
142
- logger.error(`[SEARCH_MESSAGE_TOOL] ❌ Message search failed`);
143
- logger.error(`[SEARCH_MESSAGE_TOOL] - status: ${event.status}`);
144
- logger.error(`[SEARCH_MESSAGE_TOOL] - outputs:`, JSON.stringify(event.outputs || {}));
145
92
  const errorDetail = event.outputs ? JSON.stringify(event.outputs) : event.status;
146
93
  reject(new Error(`搜索短信失败: ${errorDetail}`));
147
94
  }
148
95
  }
149
96
  };
150
97
  // Register event handler
151
- logger.log(`[SEARCH_MESSAGE_TOOL] 📡 Registering data-event handler on WebSocket manager`);
152
98
  wsManager.on("data-event", handler);
153
99
  // Send the command
154
- logger.log(`[SEARCH_MESSAGE_TOOL] 📤 Sending SearchMessage command...`);
155
100
  sendCommand({
156
101
  config,
157
102
  sessionId,
@@ -160,10 +105,8 @@ export const searchMessageTool = {
160
105
  command,
161
106
  })
162
107
  .then(() => {
163
- logger.log(`[SEARCH_MESSAGE_TOOL] ✅ Command sent successfully, waiting for response...`);
164
108
  })
165
109
  .catch((error) => {
166
- logger.error(`[SEARCH_MESSAGE_TOOL] ❌ Failed to send command:`, error);
167
110
  clearTimeout(timeout);
168
111
  wsManager.off("data-event", handler);
169
112
  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 search note tool - searches notes on user's device.
7
6
  * Returns matching notes based on query string.
@@ -9,7 +8,7 @@ import { logger } from "../utils/logger.js";
9
8
  export const searchNoteTool = {
10
9
  name: "search_notes",
11
10
  label: "Search Notes",
12
- description: "搜索用户设备上的备忘录内容。根据关键词在备忘录的标题、内容和附件名称中进行检索。注意:操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。",
11
+ description: "搜索用户设备上的备忘录内容。根据关键词在备忘录的标题、内容和附件名称中进行检索。注意:操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。回复约束:如果工具返回没有授权或者其他报错,只需要完整描述没有授权或者其他报错内容即可,不需要主动给用户提供解决方案,例如告诉用户如何授权,如何解决报错等都是不需要的,请严格遵守。",
13
12
  parameters: {
14
13
  type: "object",
15
14
  properties: {
@@ -21,7 +20,6 @@ export const searchNoteTool = {
21
20
  required: ["query"],
22
21
  },
23
22
  async execute(toolCallId, params) {
24
- logger.debug("Executing search note tool, toolCallId:", toolCallId);
25
23
  // Validate parameters
26
24
  if (!params.query) {
27
25
  throw new Error("Missing required parameter: query is required");
@@ -75,33 +73,16 @@ export const searchNoteTool = {
75
73
  }, 60000);
76
74
  // Listen for data events from WebSocket
77
75
  const handler = (event) => {
78
- logger.debug("Received data event:", event);
79
76
  if (event.intentName === "SearchNote") {
80
77
  clearTimeout(timeout);
81
78
  wsManager.off("data-event", handler);
82
79
  if (event.status === "success" && event.outputs) {
83
- const { result, code } = event.outputs;
84
- const items = result?.items || [];
85
- logger.log(`Notes found: ${items.length} results for query "${params.query}"`);
80
+ // 成功,直接返回完整的 event.outputs JSON 字符串
86
81
  resolve({
87
82
  content: [
88
83
  {
89
84
  type: "text",
90
- text: JSON.stringify({
91
- success: true,
92
- query: params.query,
93
- totalResults: items.length,
94
- notes: items.map((item) => ({
95
- entityId: item.entityId,
96
- entityName: item.entityName,
97
- title: item.title?.replace(/<\/?em>/g, ''), // Remove <em> tags
98
- content: item.content,
99
- createdDate: item.createdDate,
100
- modifiedDate: item.modifiedDate,
101
- })),
102
- indexName: result?.indexName,
103
- code,
104
- }),
85
+ text: JSON.stringify(event.outputs),
105
86
  },
106
87
  ],
107
88
  });
@@ -120,6 +101,7 @@ export const searchNoteTool = {
120
101
  taskId,
121
102
  messageId,
122
103
  command,
104
+ }).then(() => {
123
105
  }).catch((error) => {
124
106
  clearTimeout(timeout);
125
107
  wsManager.off("data-event", handler);
@@ -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 search photo gallery tool - searches photos in user's gallery.
7
6
  * Returns local mediaUri strings that can be used with upload_photo tool.
@@ -14,7 +13,7 @@ export const searchPhotoGalleryTool = {
14
13
  label: "Search Photo Gallery",
15
14
  description: `插件功能描述:搜索用户手机图库中的照片
16
15
 
17
- 工具使用约束:如果用户说从手机图库中或者从相册中查询xx图片时调用此工具。
16
+ 工具使用约束:如果用户说从手机图库中或者从相册中查询xx图片时调用此工具,注意此工具仅支持从本地图库检索,不支持云空间相册检索。
18
17
 
19
18
  工具输入输出简介:
20
19
  a. 根据图像描述语料检索匹配的照片,返回照片在手机本地的 mediaUri以及thumbnailUri。
@@ -43,6 +42,8 @@ export const searchPhotoGalleryTool = {
43
42
  a. 只有当用户明确表达从手机相册搜索或者从图库搜索时才执行此工具,如果用户仅表达要搜索xxx图片,并没有说明搜索数据源,则不要贸然调用此插件,可以优先尝试websearch或者询问用户是否要从手机图库中搜索。
44
43
  b. 操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。
45
44
  c. 如果用户请求包含多个实体或时间范围,需要主动拆分成多次查询并告知用户。
45
+
46
+ 回复约束:如果工具返回没有授权或者其他报错,只需要完整描述没有授权或者其他报错内容即可,不需要主动给用户提供解决方案,例如告诉用户如何授权,如何解决报错等都是不需要的,请严格遵守。
46
47
  `,
47
48
  parameters: {
48
49
  type: "object",
@@ -63,61 +64,25 @@ export const searchPhotoGalleryTool = {
63
64
  required: ["query"],
64
65
  },
65
66
  async execute(toolCallId, params) {
66
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 🚀 Starting execution`);
67
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] - toolCallId: ${toolCallId}`);
68
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] - params:`, JSON.stringify(params));
69
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] - timestamp: ${new Date().toISOString()}`);
70
67
  // Validate parameters
71
68
  if (!params.query) {
72
- logger.error(`[SEARCH_PHOTO_GALLERY_TOOL] ❌ Missing required parameter: query`);
73
69
  throw new Error("Missing required parameter: query is required");
74
70
  }
75
71
  // Get session context
76
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 🔍 Attempting to get session context...`);
77
72
  const sessionContext = getCurrentSessionContext();
78
73
  if (!sessionContext) {
79
- logger.error(`[SEARCH_PHOTO_GALLERY_TOOL] ❌ FAILED: No active session found!`);
80
- logger.error(`[SEARCH_PHOTO_GALLERY_TOOL] - toolCallId: ${toolCallId}`);
81
74
  throw new Error("No active XY session found. Search photo gallery tool can only be used during an active conversation.");
82
75
  }
83
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] ✅ Session context found`);
84
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] - sessionId: ${sessionContext.sessionId}`);
85
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] - taskId: ${sessionContext.taskId}`);
86
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] - messageId: ${sessionContext.messageId}`);
87
76
  const { config, sessionId, taskId, messageId } = sessionContext;
88
77
  // Get WebSocket manager
89
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 🔌 Getting WebSocket manager...`);
90
78
  const wsManager = getXYWebSocketManager(config);
91
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] ✅ WebSocket manager obtained`);
92
79
  // Search for photos
93
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 📸 Searching for photos...`);
94
- const items = await searchPhotos(wsManager, config, sessionId, taskId, messageId, params.query);
95
- if (!items || items.length === 0) {
96
- logger.warn(`[SEARCH_PHOTO_GALLERY_TOOL] ⚠️ No photos found for query: ${params.query}`);
97
- return {
98
- content: [
99
- {
100
- type: "text",
101
- text: JSON.stringify({
102
- items: [],
103
- count: 0,
104
- message: "未找到匹配的照片"
105
- }),
106
- },
107
- ],
108
- };
109
- }
110
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] ✅ Found ${items.length} photos`);
111
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] - items:`, JSON.stringify(items));
80
+ const outputs = await searchPhotos(wsManager, config, sessionId, taskId, messageId, params.query);
112
81
  return {
113
82
  content: [
114
83
  {
115
84
  type: "text",
116
- text: JSON.stringify({
117
- items,
118
- count: items.length,
119
- message: `找到 ${items.length} 张照片。注意:mediaUri 和 thumbnailUri 是本地路径,无法直接访问。如需下载或查看,请使用 upload_photo 工具。`
120
- }),
85
+ text: JSON.stringify(outputs),
121
86
  },
122
87
  ],
123
88
  };
@@ -125,10 +90,9 @@ export const searchPhotoGalleryTool = {
125
90
  };
126
91
  /**
127
92
  * Search for photos using query description
128
- * Returns array of photo items with complete information
93
+ * Returns complete event.outputs object
129
94
  */
130
95
  async function searchPhotos(wsManager, config, sessionId, taskId, messageId, query) {
131
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 📦 Building SearchPhotoVideo command...`);
132
96
  const command = {
133
97
  header: {
134
98
  namespace: "Common",
@@ -164,34 +128,23 @@ async function searchPhotos(wsManager, config, sessionId, taskId, messageId, que
164
128
  };
165
129
  return new Promise((resolve, reject) => {
166
130
  const timeout = setTimeout(() => {
167
- logger.error(`[SEARCH_PHOTO_GALLERY_TOOL] ⏰ Timeout: No response for SearchPhotoVideo within 60 seconds`);
168
131
  wsManager.off("data-event", handler);
169
132
  reject(new Error("搜索照片超时(60秒)"));
170
133
  }, 60000);
171
134
  const handler = (event) => {
172
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 📨 Received data event:`, JSON.stringify(event));
173
135
  if (event.intentName === "SearchPhotoVideo") {
174
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 🎯 SearchPhotoVideo event received`);
175
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] - status: ${event.status}`);
176
136
  clearTimeout(timeout);
177
137
  wsManager.off("data-event", handler);
178
138
  if (event.status === "success" && event.outputs) {
179
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] Photo search completed successfully`);
180
- const result = event.outputs.result;
181
- const items = result?.items || [];
182
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 📊 Found ${items.length} photo items`);
183
- resolve(items);
139
+ // 成功,直接返回完整的 event.outputs
140
+ resolve(event.outputs);
184
141
  }
185
142
  else {
186
- logger.error(`[SEARCH_PHOTO_GALLERY_TOOL] ❌ Photo search failed`);
187
- logger.error(`[SEARCH_PHOTO_GALLERY_TOOL] - status: ${event.status}`);
188
143
  reject(new Error(`搜索照片失败: ${event.status}`));
189
144
  }
190
145
  }
191
146
  };
192
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 📡 Registering data-event handler for SearchPhotoVideo`);
193
147
  wsManager.on("data-event", handler);
194
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 📤 Sending SearchPhotoVideo command...`);
195
148
  sendCommand({
196
149
  config,
197
150
  sessionId,
@@ -200,10 +153,8 @@ async function searchPhotos(wsManager, config, sessionId, taskId, messageId, que
200
153
  command,
201
154
  })
202
155
  .then(() => {
203
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] ✅ SearchPhotoVideo command sent successfully`);
204
156
  })
205
157
  .catch((error) => {
206
- logger.error(`[SEARCH_PHOTO_GALLERY_TOOL] ❌ Failed to send SearchPhotoVideo command:`, error);
207
158
  clearTimeout(timeout);
208
159
  wsManager.off("data-event", handler);
209
160
  reject(error);
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Send command to car (小艺车机) tool - sends an output command to the car's Xiaoyi system.
3
+ * The command will be received and executed on the car's Xiaoyi device.
4
+ */
5
+ export declare const sendCommandToCarTool: any;
@@ -0,0 +1,85 @@
1
+ // Send Command To Car tool implementation
2
+ import { sendCommand } from "../formatter.js";
3
+ import { getCurrentSessionContext } from "./session-manager.js";
4
+ import { logger } from "../utils/logger.js";
5
+ /**
6
+ * Send command to car (小艺车机) tool - sends an output command to the car's Xiaoyi system.
7
+ * The command will be received and executed on the car's Xiaoyi device.
8
+ */
9
+ export const sendCommandToCarTool = {
10
+ name: "send_command_to_car",
11
+ label: "Send Command To Car",
12
+ description: "将输出指令发送给小艺车机,车机小艺会接收并执行该指令。注意:请勿重复调用此工具,如果超时或失败,最多重试一次。回复约束:如果工具返回没有授权或者其他报错,只需要完整描述没有授权或者其他报错内容即可,不需要主动给用户提供解决方案。",
13
+ parameters: {
14
+ type: "object",
15
+ properties: {
16
+ command: {
17
+ type: "string",
18
+ description: "要发送给车机的指令内容(对应intentParam中的out字段)",
19
+ },
20
+ },
21
+ required: ["command"],
22
+ },
23
+ async execute(toolCallId, params) {
24
+ // Validate command parameter
25
+ if (!params.command || typeof params.command !== "string" || params.command.trim() === "") {
26
+ throw new Error("Missing required parameter: command must be a non-empty string");
27
+ }
28
+ // Get session context
29
+ const sessionContext = getCurrentSessionContext();
30
+ if (!sessionContext) {
31
+ throw new Error("No active XY session found. Send command to car tool can only be used during an active conversation.");
32
+ }
33
+ const { config, sessionId, taskId, messageId } = sessionContext;
34
+ // Build PlayStoryBook command
35
+ const command = {
36
+ header: {
37
+ namespace: "Common",
38
+ name: "Action",
39
+ },
40
+ payload: {
41
+ cardParam: {},
42
+ executeParam: {
43
+ achieveType: "INTENT",
44
+ actionResponse: true,
45
+ bundleName: "com.huawei.vassistantcar",
46
+ dimension: "",
47
+ executeMode: "background",
48
+ intentName: "PlayStoryBook",
49
+ intentParam: {
50
+ out: params.command,
51
+ },
52
+ needUnlock: true,
53
+ permissionId: [],
54
+ timeOut: 5,
55
+ },
56
+ needUploadResult: true,
57
+ pageControlRelated: false,
58
+ responses: [
59
+ {
60
+ displayText: "",
61
+ resultCode: "",
62
+ ttsText: "",
63
+ },
64
+ ],
65
+ },
66
+ };
67
+ // Send command - fire and forget, return success once sent
68
+ await sendCommand({
69
+ config,
70
+ sessionId,
71
+ taskId,
72
+ messageId,
73
+ command,
74
+ });
75
+ logger.log("[sendCommandToCar] command sent to car successfully");
76
+ return {
77
+ content: [
78
+ {
79
+ type: "text",
80
+ text: JSON.stringify({ success: true, message: "指令已成功下发给车机" }),
81
+ },
82
+ ],
83
+ };
84
+ },
85
+ };