@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.
- package/dist/index.d.ts +0 -2
- package/dist/index.js +42 -2
- package/dist/src/bot.d.ts +1 -0
- package/dist/src/bot.js +59 -61
- package/dist/src/channel.js +30 -4
- package/dist/src/client.js +0 -9
- package/dist/src/cspl/call-api.d.ts +3 -0
- package/dist/src/cspl/call-api.js +86 -0
- package/dist/src/cspl/config.d.ts +19 -0
- package/dist/src/cspl/config.js +50 -0
- package/dist/src/cspl/constants.d.ts +43 -0
- package/dist/src/cspl/constants.js +22 -0
- package/dist/src/cspl/utils.d.ts +10 -0
- package/dist/src/cspl/utils.js +57 -0
- package/dist/src/file-upload.d.ts +5 -0
- package/dist/src/file-upload.js +88 -6
- package/dist/src/formatter.d.ts +2 -0
- package/dist/src/formatter.js +13 -30
- package/dist/src/heartbeat.js +0 -4
- package/dist/src/monitor.js +8 -10
- package/dist/src/onboarding.d.ts +3 -4
- package/dist/src/onboarding.js +2 -2
- package/dist/src/outbound.d.ts +2 -1
- package/dist/src/outbound.js +1 -19
- package/dist/src/parser.d.ts +6 -0
- package/dist/src/parser.js +16 -0
- package/dist/src/push.js +0 -21
- package/dist/src/reply-dispatcher.d.ts +4 -0
- package/dist/src/reply-dispatcher.js +46 -8
- package/dist/src/steer-injector.d.ts +16 -0
- package/dist/src/steer-injector.js +74 -0
- package/dist/src/thread-bindings.d.ts +54 -0
- package/dist/src/thread-bindings.js +214 -0
- package/dist/src/tools/calendar-tool.js +2 -37
- package/dist/src/tools/call-phone-tool.js +3 -60
- package/dist/src/tools/create-alarm-tool.js +8 -109
- package/dist/src/tools/delete-alarm-tool.js +5 -69
- package/dist/src/tools/device-tool-map.d.ts +4 -0
- package/dist/src/tools/device-tool-map.js +24 -0
- package/dist/src/tools/image-reading-tool.d.ts +5 -0
- package/dist/src/tools/image-reading-tool.js +328 -0
- package/dist/src/tools/location-tool.js +6 -40
- package/dist/src/tools/modify-alarm-tool.js +8 -114
- package/dist/src/tools/modify-note-tool.js +3 -41
- package/dist/src/tools/note-tool.js +4 -16
- package/dist/src/tools/search-alarm-tool.js +12 -118
- package/dist/src/tools/search-calendar-tool.js +4 -81
- package/dist/src/tools/search-contact-tool.js +2 -55
- package/dist/src/tools/search-file-tool.js +4 -61
- package/dist/src/tools/search-message-tool.js +2 -59
- package/dist/src/tools/search-note-tool.js +4 -22
- package/dist/src/tools/search-photo-gallery-tool.js +8 -57
- package/dist/src/tools/send-command-to-car-tool.d.ts +5 -0
- package/dist/src/tools/send-command-to-car-tool.js +85 -0
- package/dist/src/tools/send-file-to-user-tool.js +1 -39
- package/dist/src/tools/send-message-tool.js +5 -56
- package/dist/src/tools/session-manager.d.ts +1 -0
- package/dist/src/tools/session-manager.js +0 -45
- package/dist/src/tools/timestamp-to-utc8-tool.d.ts +12 -0
- package/dist/src/tools/timestamp-to-utc8-tool.js +104 -0
- package/dist/src/tools/upload-file-tool.js +0 -49
- package/dist/src/tools/upload-photo-tool.js +0 -42
- package/dist/src/tools/view-push-result-tool.js +0 -11
- package/dist/src/tools/xiaoyi-collection-tool.js +4 -82
- package/dist/src/tools/xiaoyi-gui-tool.js +0 -34
- package/dist/src/utils/runtime-manager.d.ts +7 -0
- package/dist/src/utils/runtime-manager.js +42 -0
- package/dist/src/websocket.js +33 -13
- package/package.json +3 -4
- package/dist/src/tools/search-photo-tool.d.ts +0 -9
- package/dist/src/tools/search-photo-tool.js +0 -270
- package/dist/src/utils/session.d.ts +0 -34
- 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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
180
|
-
|
|
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,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
|
+
};
|