@ynhcj/xiaoyi-channel 0.0.26-beta → 0.0.26-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 (86) hide show
  1. package/dist/index.d.ts +0 -2
  2. package/dist/index.js +48 -2
  3. package/dist/src/bot.js +81 -28
  4. package/dist/src/channel.js +33 -7
  5. package/dist/src/client.js +11 -27
  6. package/dist/src/config.js +2 -2
  7. package/dist/src/cspl/call-api.d.ts +3 -0
  8. package/dist/src/cspl/call-api.js +107 -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 +92 -0
  17. package/dist/src/formatter.d.ts +14 -0
  18. package/dist/src/formatter.js +49 -25
  19. package/dist/src/heartbeat.js +0 -4
  20. package/dist/src/monitor.js +12 -0
  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 +52 -6
  25. package/dist/src/parser.d.ts +7 -0
  26. package/dist/src/parser.js +22 -0
  27. package/dist/src/push.d.ts +8 -1
  28. package/dist/src/push.js +30 -22
  29. package/dist/src/reply-dispatcher.js +39 -5
  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 +7 -40
  35. package/dist/src/tools/call-phone-tool.d.ts +5 -0
  36. package/dist/src/tools/call-phone-tool.js +142 -0
  37. package/dist/src/tools/create-alarm-tool.d.ts +7 -0
  38. package/dist/src/tools/create-alarm-tool.js +349 -0
  39. package/dist/src/tools/delete-alarm-tool.d.ts +11 -0
  40. package/dist/src/tools/delete-alarm-tool.js +174 -0
  41. package/dist/src/tools/image-reading-tool.d.ts +5 -0
  42. package/dist/src/tools/image-reading-tool.js +328 -0
  43. package/dist/src/tools/location-tool.js +8 -42
  44. package/dist/src/tools/modify-alarm-tool.d.ts +9 -0
  45. package/dist/src/tools/modify-alarm-tool.js +365 -0
  46. package/dist/src/tools/modify-note-tool.js +5 -43
  47. package/dist/src/tools/note-tool.js +33 -24
  48. package/dist/src/tools/search-alarm-tool.d.ts +8 -0
  49. package/dist/src/tools/search-alarm-tool.js +285 -0
  50. package/dist/src/tools/search-calendar-tool.js +10 -84
  51. package/dist/src/tools/search-contact-tool.js +4 -57
  52. package/dist/src/tools/search-file-tool.d.ts +5 -0
  53. package/dist/src/tools/search-file-tool.js +128 -0
  54. package/dist/src/tools/search-message-tool.d.ts +5 -0
  55. package/dist/src/tools/search-message-tool.js +116 -0
  56. package/dist/src/tools/search-note-tool.js +6 -24
  57. package/dist/src/tools/search-photo-gallery-tool.js +40 -61
  58. package/dist/src/tools/send-file-to-user-tool.d.ts +5 -0
  59. package/dist/src/tools/send-file-to-user-tool.js +279 -0
  60. package/dist/src/tools/send-message-tool.d.ts +5 -0
  61. package/dist/src/tools/send-message-tool.js +138 -0
  62. package/dist/src/tools/session-manager.d.ts +15 -0
  63. package/dist/src/tools/session-manager.js +43 -31
  64. package/dist/src/tools/upload-file-tool.d.ts +13 -0
  65. package/dist/src/tools/upload-file-tool.js +216 -0
  66. package/dist/src/tools/upload-photo-tool.js +2 -44
  67. package/dist/src/tools/view-push-result-tool.d.ts +5 -0
  68. package/dist/src/tools/view-push-result-tool.js +107 -0
  69. package/dist/src/tools/xiaoyi-collection-tool.d.ts +5 -0
  70. package/dist/src/tools/xiaoyi-collection-tool.js +112 -0
  71. package/dist/src/tools/xiaoyi-gui-tool.js +2 -36
  72. package/dist/src/trigger-handler.d.ts +22 -0
  73. package/dist/src/trigger-handler.js +59 -0
  74. package/dist/src/types.d.ts +1 -8
  75. package/dist/src/types.js +4 -0
  76. package/dist/src/utils/pushdata-manager.d.ts +28 -0
  77. package/dist/src/utils/pushdata-manager.js +171 -0
  78. package/dist/src/utils/pushid-manager.d.ts +12 -0
  79. package/dist/src/utils/pushid-manager.js +105 -0
  80. package/dist/src/websocket.d.ts +25 -31
  81. package/dist/src/websocket.js +233 -271
  82. package/package.json +2 -2
  83. package/dist/src/tools/search-photo-tool.d.ts +0 -9
  84. package/dist/src/tools/search-photo-tool.js +0 -270
  85. package/dist/src/utils/session.d.ts +0 -34
  86. package/dist/src/utils/session.js +0 -50
@@ -0,0 +1,128 @@
1
+ import { getXYWebSocketManager } from "../client.js";
2
+ import { sendCommand } from "../formatter.js";
3
+ import { getCurrentSessionContext } from "./session-manager.js";
4
+ /**
5
+ * XY search file tool - searches files on user's device file system.
6
+ * Returns matching files based on keyword search in file name or content.
7
+ */
8
+ export const searchFileTool = {
9
+ name: "search_file",
10
+ label: "Search File",
11
+ description: `搜索手机文件系统的文件。
12
+
13
+ 【重要】使用约束:此工具仅在用户显著说明要从手机搜索时才执行,例如:
14
+ - "从我手机里面搜索xxxx"
15
+ - "从手机文件系统找一下xxxx"
16
+ - "在手机上查找文件xxxx"
17
+ - "搜索手机里的文件"
18
+
19
+ 如果用户没有明确说明从手机搜索(如仅说"搜索文件"、"找一下xxxx"),应默认从 openclaw 本地的文件系统查询,不要调用此工具。
20
+
21
+ 功能说明:根据关键词搜索文件名称或内容,返回匹配的文件列表(包括文件名、路径、大小、修改时间等信息)。
22
+
23
+ 注意事项:操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。`,
24
+ parameters: {
25
+ type: "object",
26
+ properties: {
27
+ query: {
28
+ type: "string",
29
+ description: "搜索关键词,用于匹配文件名称、后缀名或文件内容",
30
+ },
31
+ },
32
+ required: ["query"],
33
+ },
34
+ async execute(toolCallId, params) {
35
+ // Validate query parameter
36
+ if (!params.query || typeof params.query !== "string" || params.query.trim() === "") {
37
+ throw new Error("Missing required parameter: query must be a non-empty string");
38
+ }
39
+ // Get session context
40
+ const sessionContext = getCurrentSessionContext();
41
+ if (!sessionContext) {
42
+ throw new Error("No active XY session found. Search file tool can only be used during an active conversation.");
43
+ }
44
+ const { config, sessionId, taskId, messageId } = sessionContext;
45
+ // Get WebSocket manager
46
+ const wsManager = getXYWebSocketManager(config);
47
+ // Build SearchFile command
48
+ const command = {
49
+ header: {
50
+ namespace: "Common",
51
+ name: "Action",
52
+ },
53
+ payload: {
54
+ cardParam: {},
55
+ executeParam: {
56
+ executeMode: "background",
57
+ intentName: "SearchFile",
58
+ bundleName: "com.huawei.hmos.aidispatchservice",
59
+ needUnlock: true,
60
+ actionResponse: true,
61
+ appType: "OHOS_APP",
62
+ timeOut: 5,
63
+ intentParam: {
64
+ query: params.query.trim(),
65
+ },
66
+ permissionId: [],
67
+ achieveType: "INTENT",
68
+ },
69
+ responses: [
70
+ {
71
+ resultCode: "",
72
+ displayText: "",
73
+ ttsText: "",
74
+ },
75
+ ],
76
+ needUploadResult: true,
77
+ noHalfPage: false,
78
+ pageControlRelated: false,
79
+ },
80
+ };
81
+ // Send command and wait for response (60 second timeout)
82
+ return new Promise((resolve, reject) => {
83
+ const timeout = setTimeout(() => {
84
+ wsManager.off("data-event", handler);
85
+ reject(new Error("搜索文件超时(60秒)"));
86
+ }, 60000);
87
+ // Listen for data events from WebSocket
88
+ const handler = (event) => {
89
+ if (event.intentName === "SearchFile") {
90
+ clearTimeout(timeout);
91
+ wsManager.off("data-event", handler);
92
+ if (event.status === "success" && event.outputs) {
93
+ // 成功,直接返回完整的 event.outputs JSON 字符串
94
+ resolve({
95
+ content: [
96
+ {
97
+ type: "text",
98
+ text: JSON.stringify(event.outputs),
99
+ }
100
+ ]
101
+ });
102
+ }
103
+ else {
104
+ const errorDetail = event.outputs ? JSON.stringify(event.outputs) : event.status;
105
+ reject(new Error(`搜索文件失败: ${errorDetail}`));
106
+ }
107
+ }
108
+ };
109
+ // Register event handler
110
+ wsManager.on("data-event", handler);
111
+ // Send the command
112
+ sendCommand({
113
+ config,
114
+ sessionId,
115
+ taskId,
116
+ messageId,
117
+ command,
118
+ })
119
+ .then(() => {
120
+ })
121
+ .catch((error) => {
122
+ clearTimeout(timeout);
123
+ wsManager.off("data-event", handler);
124
+ reject(error);
125
+ });
126
+ });
127
+ },
128
+ };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * XY search message tool - searches SMS messages on user's device.
3
+ * Returns matching messages based on content keyword search.
4
+ */
5
+ export declare const searchMessageTool: any;
@@ -0,0 +1,116 @@
1
+ import { getXYWebSocketManager } from "../client.js";
2
+ import { sendCommand } from "../formatter.js";
3
+ import { getCurrentSessionContext } from "./session-manager.js";
4
+ /**
5
+ * XY search message tool - searches SMS messages on user's device.
6
+ * Returns matching messages based on content keyword search.
7
+ */
8
+ export const searchMessageTool = {
9
+ name: "search_message",
10
+ label: "Search Message",
11
+ description: "搜索手机短信。根据关键词搜索短信内容。注意:操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。",
12
+ parameters: {
13
+ type: "object",
14
+ properties: {
15
+ content: {
16
+ type: "string",
17
+ description: "搜索关键词,用于在短信内容中进行匹配",
18
+ },
19
+ },
20
+ required: ["content"],
21
+ },
22
+ async execute(toolCallId, params) {
23
+ // Validate content parameter
24
+ if (!params.content || typeof params.content !== "string" || params.content.trim() === "") {
25
+ throw new Error("Missing required parameter: content must be a non-empty string");
26
+ }
27
+ // Get session context
28
+ const sessionContext = getCurrentSessionContext();
29
+ if (!sessionContext) {
30
+ throw new Error("No active XY session found. Search message tool can only be used during an active conversation.");
31
+ }
32
+ const { config, sessionId, taskId, messageId } = sessionContext;
33
+ // Get WebSocket manager
34
+ const wsManager = getXYWebSocketManager(config);
35
+ // Build SearchMessage command
36
+ const command = {
37
+ header: {
38
+ namespace: "Common",
39
+ name: "Action",
40
+ },
41
+ payload: {
42
+ cardParam: {},
43
+ executeParam: {
44
+ executeMode: "background",
45
+ intentName: "SearchMessage",
46
+ bundleName: "com.huawei.hmos.aidispatchservice",
47
+ needUnlock: true,
48
+ actionResponse: true,
49
+ appType: "OHOS_APP",
50
+ timeOut: 5,
51
+ intentParam: {
52
+ content: params.content.trim(),
53
+ },
54
+ permissionId: [],
55
+ achieveType: "INTENT",
56
+ },
57
+ responses: [
58
+ {
59
+ resultCode: "",
60
+ displayText: "",
61
+ ttsText: "",
62
+ },
63
+ ],
64
+ needUploadResult: true,
65
+ noHalfPage: false,
66
+ pageControlRelated: false,
67
+ },
68
+ };
69
+ // Send command and wait for response (60 second timeout)
70
+ return new Promise((resolve, reject) => {
71
+ const timeout = setTimeout(() => {
72
+ wsManager.off("data-event", handler);
73
+ reject(new Error("搜索短信超时(60秒)"));
74
+ }, 60000);
75
+ // Listen for data events from WebSocket
76
+ const handler = (event) => {
77
+ if (event.intentName === "SearchMessage") {
78
+ clearTimeout(timeout);
79
+ wsManager.off("data-event", handler);
80
+ if (event.status === "success" && event.outputs) {
81
+ // 成功,直接返回完整的 event.outputs JSON 字符串
82
+ resolve({
83
+ content: [
84
+ {
85
+ type: "text",
86
+ text: JSON.stringify(event.outputs),
87
+ },
88
+ ],
89
+ });
90
+ }
91
+ else {
92
+ const errorDetail = event.outputs ? JSON.stringify(event.outputs) : event.status;
93
+ reject(new Error(`搜索短信失败: ${errorDetail}`));
94
+ }
95
+ }
96
+ };
97
+ // Register event handler
98
+ wsManager.on("data-event", handler);
99
+ // Send the command
100
+ sendCommand({
101
+ config,
102
+ sessionId,
103
+ taskId,
104
+ messageId,
105
+ command,
106
+ })
107
+ .then(() => {
108
+ })
109
+ .catch((error) => {
110
+ clearTimeout(timeout);
111
+ wsManager.off("data-event", handler);
112
+ reject(error);
113
+ });
114
+ });
115
+ },
116
+ };
@@ -1,7 +1,6 @@
1
1
  import { getXYWebSocketManager } from "../client.js";
2
2
  import { sendCommand } from "../formatter.js";
3
- import { getLatestSessionContext } from "./session-manager.js";
4
- import { logger } from "../utils/logger.js";
3
+ import { getCurrentSessionContext } from "./session-manager.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,13 +20,12 @@ 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");
28
26
  }
29
27
  // Get session context
30
- const sessionContext = getLatestSessionContext();
28
+ const sessionContext = getCurrentSessionContext();
31
29
  if (!sessionContext) {
32
30
  throw new Error("No active XY session found. Search note tool can only be used during an active conversation.");
33
31
  }
@@ -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
- import { getLatestSessionContext } from "./session-manager.js";
4
- import { logger } from "../utils/logger.js";
3
+ import { getCurrentSessionContext } from "./session-manager.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.
@@ -13,83 +12,77 @@ export const searchPhotoGalleryTool = {
13
12
  name: "search_photo_gallery",
14
13
  label: "Search Photo Gallery",
15
14
  description: `插件功能描述:搜索用户手机图库中的照片
16
- 工具使用约束:如果用户说从手机图库中或者从相册中查询xx图片时调用此工具。
15
+
16
+ 工具使用约束:如果用户说从手机图库中或者从相册中查询xx图片时调用此工具,注意此工具仅支持从本地图库检索,不支持云空间相册检索。
17
+
17
18
  工具输入输出简介:
18
19
  a. 根据图像描述语料检索匹配的照片,返回照片在手机本地的 mediaUri以及thumbnailUri。
19
20
  b. 返回的 mediaUri以及thumbnailUri 是本地路径,无法直接下载或访问。如果需要下载、查看、使用或展示照片,请使用 upload_photo 工具将 mediaUri或者thumbnailUri 转换为可访问的公网 URL。
20
21
  c. mediaUri代表手机相册中的图片原图路径,图片大小比较大,清晰度比较高
21
22
  d. thumbnailUri代表手机相册中的图片缩略图路径,图片大小比较小,清晰度适中,建议在upload_photo 工具的入参中优先使用此路径,不容易引起上传超时等问题
22
-
23
+
24
+ 搜索能力边界:
25
+ a. ✅ 支持口语化输入:改写模型会自动提取姓名、种类、地点等实体,可以使用自然语言描述(如"小狗的照片"、"南京拍的风景")
26
+ b. ✅ 支持相册搜索:可以在query中包含相册名称(如"西安之行相册的照片")
27
+ c. ✅ 支持人像搜索:前提是照片有人像tag,且需要口语化描述(如"张三的照片")
28
+ d. ❌ 不支持时间相对词:不支持"最新"、"最旧"、"最早"等表述,需要使用具体时间(如"2024年的照片"而非"去年的照片")
29
+ e. ❌ 不支持多实体查询:不支持"或"逻辑和时间范围(如"南京或上海的照片"、"近三年的照片"),需要拆分成多次独立查询
30
+ f. ❌ 不支持POI逆地理映射:照片的location是门牌号,用真实场地名称可能搜不到
31
+ g. ❌ 不支持收藏感知:无法感知照片是否被收藏
32
+ h. ❌ 不支持细粒度品种:对于动物、植物等的具体品种识别能力有限
33
+ i. ⚠️ POI提取可能不准确:地名可能作为语义搜索条件,可能导致"xx湖"搜到"yy江"或"zz湾"的照片
34
+
35
+ 查询优化建议:
36
+ a. 时间查询:将"最新"、"去年"、"近三年"等转换为具体年份(如"2024年"、"2023年到2025年"需拆分成"2023年"、"2024年"、"2025年"三次查询)
37
+ b. 多条件查询:将"或"逻辑拆分成多次查询(如"南京或上海的照片"→先查"南京的照片",再查"上海的照片")
38
+ c. 实体原子化:确保每个query只包含一个原子实体(地点、人名、物品等)
39
+ d. 相册名称:如果知道相册名,直接在query中包含相册名可以提高准确度
40
+
23
41
  注意事项:
24
42
  a. 只有当用户明确表达从手机相册搜索或者从图库搜索时才执行此工具,如果用户仅表达要搜索xxx图片,并没有说明搜索数据源,则不要贸然调用此插件,可以优先尝试websearch或者询问用户是否要从手机图库中搜索。
25
43
  b. 操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。
44
+ c. 如果用户请求包含多个实体或时间范围,需要主动拆分成多次查询并告知用户。
45
+
46
+ 回复约束:如果工具返回没有授权或者其他报错,只需要完整描述没有授权或者其他报错内容即可,不需要主动给用户提供解决方案,例如告诉用户如何授权,如何解决报错等都是不需要的,请严格遵守。
26
47
  `,
27
48
  parameters: {
28
49
  type: "object",
29
50
  properties: {
30
51
  query: {
31
52
  type: "string",
32
- description: "图像描述语料,用于检索匹配的照片(例如:'小狗的照片'、'带有键盘的图片'等)",
53
+ description: `图像描述语料,用于检索匹配的照片。支持口语化输入,会自动提取姓名、种类、地点等实体。
54
+
55
+ 使用示例:
56
+ - 正确:"小狗的照片"、"南京拍的风景"、"张三的照片"、"西安之行相册的照片"、"2024年的照片"
57
+ - 错误:"最新的照片"(应改为具体年份如"2024年的照片")
58
+ - 错误:"南京或上海的照片"(需拆分成两次查询:"南京的照片" 和 "上海的照片")
59
+ - 错误:"近三年的照片"(需拆分成"2023年的照片"、"2024年的照片"、"2025年的照片")
60
+
61
+ 重要:每次查询只能包含一个原子实体(单个地点、单个人名、单个年份等),不支持多实体或"或"逻辑。`,
33
62
  },
34
63
  },
35
64
  required: ["query"],
36
65
  },
37
66
  async execute(toolCallId, params) {
38
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 🚀 Starting execution`);
39
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] - toolCallId: ${toolCallId}`);
40
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] - params:`, JSON.stringify(params));
41
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] - timestamp: ${new Date().toISOString()}`);
42
67
  // Validate parameters
43
68
  if (!params.query) {
44
- logger.error(`[SEARCH_PHOTO_GALLERY_TOOL] ❌ Missing required parameter: query`);
45
69
  throw new Error("Missing required parameter: query is required");
46
70
  }
47
71
  // Get session context
48
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 🔍 Attempting to get session context...`);
49
- const sessionContext = getLatestSessionContext();
72
+ const sessionContext = getCurrentSessionContext();
50
73
  if (!sessionContext) {
51
- logger.error(`[SEARCH_PHOTO_GALLERY_TOOL] ❌ FAILED: No active session found!`);
52
- logger.error(`[SEARCH_PHOTO_GALLERY_TOOL] - toolCallId: ${toolCallId}`);
53
74
  throw new Error("No active XY session found. Search photo gallery tool can only be used during an active conversation.");
54
75
  }
55
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] ✅ Session context found`);
56
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] - sessionId: ${sessionContext.sessionId}`);
57
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] - taskId: ${sessionContext.taskId}`);
58
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] - messageId: ${sessionContext.messageId}`);
59
76
  const { config, sessionId, taskId, messageId } = sessionContext;
60
77
  // Get WebSocket manager
61
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 🔌 Getting WebSocket manager...`);
62
78
  const wsManager = getXYWebSocketManager(config);
63
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] ✅ WebSocket manager obtained`);
64
79
  // Search for photos
65
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 📸 Searching for photos...`);
66
- const items = await searchPhotos(wsManager, config, sessionId, taskId, messageId, params.query);
67
- if (!items || items.length === 0) {
68
- logger.warn(`[SEARCH_PHOTO_GALLERY_TOOL] ⚠️ No photos found for query: ${params.query}`);
69
- return {
70
- content: [
71
- {
72
- type: "text",
73
- text: JSON.stringify({
74
- items: [],
75
- count: 0,
76
- message: "未找到匹配的照片"
77
- }),
78
- },
79
- ],
80
- };
81
- }
82
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] ✅ Found ${items.length} photos`);
83
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] - items:`, JSON.stringify(items));
80
+ const outputs = await searchPhotos(wsManager, config, sessionId, taskId, messageId, params.query);
84
81
  return {
85
82
  content: [
86
83
  {
87
84
  type: "text",
88
- text: JSON.stringify({
89
- items,
90
- count: items.length,
91
- message: `找到 ${items.length} 张照片。注意:mediaUri 和 thumbnailUri 是本地路径,无法直接访问。如需下载或查看,请使用 upload_photo 工具。`
92
- }),
85
+ text: JSON.stringify(outputs),
93
86
  },
94
87
  ],
95
88
  };
@@ -97,10 +90,9 @@ export const searchPhotoGalleryTool = {
97
90
  };
98
91
  /**
99
92
  * Search for photos using query description
100
- * Returns array of photo items with complete information
93
+ * Returns complete event.outputs object
101
94
  */
102
95
  async function searchPhotos(wsManager, config, sessionId, taskId, messageId, query) {
103
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 📦 Building SearchPhotoVideo command...`);
104
96
  const command = {
105
97
  header: {
106
98
  namespace: "Common",
@@ -136,34 +128,23 @@ async function searchPhotos(wsManager, config, sessionId, taskId, messageId, que
136
128
  };
137
129
  return new Promise((resolve, reject) => {
138
130
  const timeout = setTimeout(() => {
139
- logger.error(`[SEARCH_PHOTO_GALLERY_TOOL] ⏰ Timeout: No response for SearchPhotoVideo within 60 seconds`);
140
131
  wsManager.off("data-event", handler);
141
132
  reject(new Error("搜索照片超时(60秒)"));
142
133
  }, 60000);
143
134
  const handler = (event) => {
144
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 📨 Received data event:`, JSON.stringify(event));
145
135
  if (event.intentName === "SearchPhotoVideo") {
146
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 🎯 SearchPhotoVideo event received`);
147
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] - status: ${event.status}`);
148
136
  clearTimeout(timeout);
149
137
  wsManager.off("data-event", handler);
150
138
  if (event.status === "success" && event.outputs) {
151
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] Photo search completed successfully`);
152
- const result = event.outputs.result;
153
- const items = result?.items || [];
154
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 📊 Found ${items.length} photo items`);
155
- resolve(items);
139
+ // 成功,直接返回完整的 event.outputs
140
+ resolve(event.outputs);
156
141
  }
157
142
  else {
158
- logger.error(`[SEARCH_PHOTO_GALLERY_TOOL] ❌ Photo search failed`);
159
- logger.error(`[SEARCH_PHOTO_GALLERY_TOOL] - status: ${event.status}`);
160
143
  reject(new Error(`搜索照片失败: ${event.status}`));
161
144
  }
162
145
  }
163
146
  };
164
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 📡 Registering data-event handler for SearchPhotoVideo`);
165
147
  wsManager.on("data-event", handler);
166
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 📤 Sending SearchPhotoVideo command...`);
167
148
  sendCommand({
168
149
  config,
169
150
  sessionId,
@@ -172,10 +153,8 @@ async function searchPhotos(wsManager, config, sessionId, taskId, messageId, que
172
153
  command,
173
154
  })
174
155
  .then(() => {
175
- logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] ✅ SearchPhotoVideo command sent successfully`);
176
156
  })
177
157
  .catch((error) => {
178
- logger.error(`[SEARCH_PHOTO_GALLERY_TOOL] ❌ Failed to send SearchPhotoVideo command:`, error);
179
158
  clearTimeout(timeout);
180
159
  wsManager.off("data-event", handler);
181
160
  reject(error);
@@ -0,0 +1,5 @@
1
+ /**
2
+ * XY send file to user tool - sends local files or remote files to user's device.
3
+ * Supports both local file paths and remote URLs.
4
+ */
5
+ export declare const sendFileToUserTool: any;