@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
|
// Enum definitions for alarm search parameters
|
|
6
5
|
const RANGE_TYPE_VALUES = ["all", "next", "current"];
|
|
7
6
|
const ALARM_STATE_VALUES = [0, 1];
|
|
@@ -33,7 +32,9 @@ export const searchAlarmTool = {
|
|
|
33
32
|
|
|
34
33
|
注意:
|
|
35
34
|
a. 操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。
|
|
36
|
-
b.
|
|
35
|
+
b. 使用该工具之前需获取当前真实时间
|
|
36
|
+
|
|
37
|
+
回复约束:如果工具返回没有授权或者其他报错,只需要完整描述没有授权或者其他报错内容即可,不需要主动给用户提供解决方案,例如告诉用户如何授权,如何解决报错等都是不需要的,请严格遵守。`,
|
|
37
38
|
parameters: {
|
|
38
39
|
type: "object",
|
|
39
40
|
properties: {
|
|
@@ -63,10 +64,6 @@ b. 使用该工具之前需获取当前真实时间`,
|
|
|
63
64
|
},
|
|
64
65
|
},
|
|
65
66
|
async execute(toolCallId, params) {
|
|
66
|
-
logger.log(`[SEARCH_ALARM_TOOL] 🚀 Starting execution`);
|
|
67
|
-
logger.log(`[SEARCH_ALARM_TOOL] - toolCallId: ${toolCallId}`);
|
|
68
|
-
logger.log(`[SEARCH_ALARM_TOOL] - params:`, JSON.stringify(params));
|
|
69
|
-
logger.log(`[SEARCH_ALARM_TOOL] - timestamp: ${new Date().toISOString()}`);
|
|
70
67
|
// ===== Validate at least one search criterion is provided =====
|
|
71
68
|
const hasRangeType = params.rangeType !== undefined && params.rangeType !== null;
|
|
72
69
|
const hasAlarmState = params.alarmState !== undefined && params.alarmState !== null;
|
|
@@ -74,92 +71,64 @@ b. 使用该工具之前需获取当前真实时间`,
|
|
|
74
71
|
const hasStartTime = params.startTime !== undefined && params.startTime !== null;
|
|
75
72
|
const hasEndTime = params.endTime !== undefined && params.endTime !== null;
|
|
76
73
|
if (!hasRangeType && !hasAlarmState && !hasDaysOfWakeType && !hasStartTime && !hasEndTime) {
|
|
77
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ No search criteria provided`);
|
|
78
74
|
throw new Error("至少需要提供一个检索条件:rangeType、alarmState、daysOfWakeType 或时间范围(startTime + endTime)");
|
|
79
75
|
}
|
|
80
76
|
// ===== Validate rangeType =====
|
|
81
77
|
if (hasRangeType) {
|
|
82
78
|
if (typeof params.rangeType !== "string") {
|
|
83
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ Invalid rangeType type`);
|
|
84
79
|
throw new Error("rangeType must be a string");
|
|
85
80
|
}
|
|
86
81
|
if (!RANGE_TYPE_VALUES.includes(params.rangeType)) {
|
|
87
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ Invalid rangeType value: ${params.rangeType}`);
|
|
88
82
|
throw new Error(`rangeType must be one of: ${RANGE_TYPE_VALUES.join(", ")}`);
|
|
89
83
|
}
|
|
90
|
-
logger.log(`[SEARCH_ALARM_TOOL] - rangeType: ${params.rangeType}`);
|
|
91
84
|
}
|
|
92
85
|
// ===== Validate alarmState =====
|
|
93
86
|
if (hasAlarmState) {
|
|
94
87
|
if (typeof params.alarmState !== "number") {
|
|
95
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ Invalid alarmState type`);
|
|
96
88
|
throw new Error("alarmState must be a number");
|
|
97
89
|
}
|
|
98
90
|
if (!ALARM_STATE_VALUES.includes(params.alarmState)) {
|
|
99
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ Invalid alarmState value: ${params.alarmState}`);
|
|
100
91
|
throw new Error(`alarmState must be one of: ${ALARM_STATE_VALUES.join(", ")}`);
|
|
101
92
|
}
|
|
102
|
-
logger.log(`[SEARCH_ALARM_TOOL] - alarmState: ${params.alarmState}`);
|
|
103
93
|
}
|
|
104
94
|
// ===== Validate daysOfWakeType =====
|
|
105
95
|
if (hasDaysOfWakeType) {
|
|
106
96
|
if (typeof params.daysOfWakeType !== "number") {
|
|
107
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ Invalid daysOfWakeType type`);
|
|
108
97
|
throw new Error("daysOfWakeType must be a number");
|
|
109
98
|
}
|
|
110
99
|
if (!DAYS_OF_WAKE_TYPE_VALUES.includes(params.daysOfWakeType)) {
|
|
111
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ Invalid daysOfWakeType value: ${params.daysOfWakeType}`);
|
|
112
100
|
throw new Error(`daysOfWakeType must be one of: ${DAYS_OF_WAKE_TYPE_VALUES.join(", ")}`);
|
|
113
101
|
}
|
|
114
|
-
logger.log(`[SEARCH_ALARM_TOOL] - daysOfWakeType: ${params.daysOfWakeType}`);
|
|
115
102
|
}
|
|
116
103
|
// ===== Validate time interval (startTime and endTime must be provided together) =====
|
|
117
104
|
if (hasStartTime !== hasEndTime) {
|
|
118
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ startTime and endTime must be provided together`);
|
|
119
105
|
throw new Error("startTime 和 endTime 必须一起提供");
|
|
120
106
|
}
|
|
121
107
|
let timeInterval = null;
|
|
122
108
|
if (hasStartTime && hasEndTime) {
|
|
123
109
|
// Parse and convert startTime and endTime to timestamps
|
|
124
|
-
logger.log(`[SEARCH_ALARM_TOOL] 🕒 Parsing time interval...`);
|
|
125
|
-
logger.log(`[SEARCH_ALARM_TOOL] - startTime input: ${params.startTime}`);
|
|
126
|
-
logger.log(`[SEARCH_ALARM_TOOL] - endTime input: ${params.endTime}`);
|
|
127
110
|
const startTimeMs = parseAlarmTimeToTimestamp(params.startTime);
|
|
128
111
|
const endTimeMs = parseAlarmTimeToTimestamp(params.endTime);
|
|
129
112
|
if (startTimeMs === null) {
|
|
130
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ Invalid startTime format`);
|
|
131
113
|
throw new Error("Invalid startTime format. Required format: YYYYMMDD hhmmss (e.g., 20240315 000000)");
|
|
132
114
|
}
|
|
133
115
|
if (endTimeMs === null) {
|
|
134
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ Invalid endTime format`);
|
|
135
116
|
throw new Error("Invalid endTime format. Required format: YYYYMMDD hhmmss (e.g., 20240315 235959)");
|
|
136
117
|
}
|
|
137
118
|
if (startTimeMs >= endTimeMs) {
|
|
138
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ startTime must be before endTime`);
|
|
139
119
|
throw new Error("startTime 必须早于 endTime");
|
|
140
120
|
}
|
|
141
121
|
timeInterval = [startTimeMs, endTimeMs];
|
|
142
|
-
logger.log(`[SEARCH_ALARM_TOOL] ✅ Time interval parsed: [${startTimeMs}, ${endTimeMs}]`);
|
|
143
122
|
}
|
|
144
123
|
// Get session context
|
|
145
|
-
logger.log(`[SEARCH_ALARM_TOOL] 🔍 Attempting to get session context...`);
|
|
146
124
|
const sessionContext = getCurrentSessionContext();
|
|
147
125
|
if (!sessionContext) {
|
|
148
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ FAILED: No active session found!`);
|
|
149
|
-
logger.error(`[SEARCH_ALARM_TOOL] - toolCallId: ${toolCallId}`);
|
|
150
126
|
throw new Error("No active XY session found. Search alarm tool can only be used during an active conversation.");
|
|
151
127
|
}
|
|
152
|
-
logger.log(`[SEARCH_ALARM_TOOL] ✅ Session context found`);
|
|
153
|
-
logger.log(`[SEARCH_ALARM_TOOL] - sessionId: ${sessionContext.sessionId}`);
|
|
154
|
-
logger.log(`[SEARCH_ALARM_TOOL] - taskId: ${sessionContext.taskId}`);
|
|
155
|
-
logger.log(`[SEARCH_ALARM_TOOL] - messageId: ${sessionContext.messageId}`);
|
|
156
128
|
const { config, sessionId, taskId, messageId } = sessionContext;
|
|
157
129
|
// Get WebSocket manager
|
|
158
|
-
logger.log(`[SEARCH_ALARM_TOOL] 🔌 Getting WebSocket manager...`);
|
|
159
130
|
const wsManager = getXYWebSocketManager(config);
|
|
160
|
-
logger.log(`[SEARCH_ALARM_TOOL] ✅ WebSocket manager obtained`);
|
|
161
131
|
// Build SearchAlarm command
|
|
162
|
-
logger.log(`[SEARCH_ALARM_TOOL] 📦 Building SearchAlarm command...`);
|
|
163
132
|
// Build intentParam with provided search criteria
|
|
164
133
|
const intentParam = {};
|
|
165
134
|
if (hasRangeType) {
|
|
@@ -206,96 +175,35 @@ b. 使用该工具之前需获取当前真实时间`,
|
|
|
206
175
|
},
|
|
207
176
|
};
|
|
208
177
|
// Send command and wait for response (60 second timeout)
|
|
209
|
-
logger.log(`[SEARCH_ALARM_TOOL] ⏳ Setting up promise to wait for alarm search response...`);
|
|
210
|
-
logger.log(`[SEARCH_ALARM_TOOL] - Timeout: 60 seconds`);
|
|
211
178
|
return new Promise((resolve, reject) => {
|
|
212
179
|
const timeout = setTimeout(() => {
|
|
213
|
-
logger.error(`[SEARCH_ALARM_TOOL] ⏰ Timeout: No response received within 60 seconds`);
|
|
214
180
|
wsManager.off("data-event", handler);
|
|
215
181
|
reject(new Error("检索闹钟超时(60秒)"));
|
|
216
182
|
}, 60000);
|
|
217
183
|
// Listen for data events from WebSocket
|
|
218
184
|
const handler = (event) => {
|
|
219
|
-
logger.log(`[SEARCH_ALARM_TOOL] 📨 Received data event:`, JSON.stringify(event));
|
|
220
185
|
if (event.intentName === "SearchAlarm") {
|
|
221
|
-
logger.log(`[SEARCH_ALARM_TOOL] 🎯 SearchAlarm event received`);
|
|
222
|
-
logger.log(`[SEARCH_ALARM_TOOL] - status: ${event.status}`);
|
|
223
186
|
clearTimeout(timeout);
|
|
224
187
|
wsManager.off("data-event", handler);
|
|
225
188
|
if (event.status === "success" && event.outputs) {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
reject(new Error(`检索闹钟失败: ${errorMsg} (错误代码: ${code})`));
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
// Extract result.items with safe checks
|
|
239
|
-
const result = event.outputs.result;
|
|
240
|
-
let items = [];
|
|
241
|
-
if (result && typeof result === "object" && Array.isArray(result.items)) {
|
|
242
|
-
items = result.items;
|
|
243
|
-
logger.log(`[SEARCH_ALARM_TOOL] 📋 Found ${items.length} alarm(s)`);
|
|
244
|
-
// Parse JSON strings in items array
|
|
245
|
-
// Items are returned as JSON strings that need to be parsed
|
|
246
|
-
const parsedItems = items.map((itemStr, index) => {
|
|
247
|
-
if (typeof itemStr !== "string") {
|
|
248
|
-
logger.warn(`[SEARCH_ALARM_TOOL] ⚠️ Item at index ${index} is not a string:`, typeof itemStr);
|
|
249
|
-
return null;
|
|
250
|
-
}
|
|
251
|
-
try {
|
|
252
|
-
const parsed = JSON.parse(itemStr);
|
|
253
|
-
logger.log(`[SEARCH_ALARM_TOOL] 📋 Parsed alarm [${index}]:`, JSON.stringify(parsed));
|
|
254
|
-
return parsed;
|
|
255
|
-
}
|
|
256
|
-
catch (parseError) {
|
|
257
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ Failed to parse item at index ${index}:`, parseError);
|
|
258
|
-
logger.error(`[SEARCH_ALARM_TOOL] - itemStr: ${itemStr}`);
|
|
259
|
-
return null;
|
|
260
|
-
}
|
|
261
|
-
}).filter((item) => item !== null);
|
|
262
|
-
logger.log(`[SEARCH_ALARM_TOOL] 🎉 Successfully parsed ${parsedItems.length} alarm(s)`);
|
|
263
|
-
resolve({
|
|
264
|
-
content: [
|
|
265
|
-
{
|
|
266
|
-
type: "text",
|
|
267
|
-
text: JSON.stringify(parsedItems),
|
|
268
|
-
},
|
|
269
|
-
],
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
else {
|
|
273
|
-
logger.warn(`[SEARCH_ALARM_TOOL] ⚠️ No items found in result or result is invalid`);
|
|
274
|
-
logger.warn(`[SEARCH_ALARM_TOOL] - result:`, JSON.stringify(result || {}));
|
|
275
|
-
// Return empty array
|
|
276
|
-
resolve({
|
|
277
|
-
content: [
|
|
278
|
-
{
|
|
279
|
-
type: "text",
|
|
280
|
-
text: "[]",
|
|
281
|
-
},
|
|
282
|
-
],
|
|
283
|
-
});
|
|
284
|
-
}
|
|
189
|
+
// 成功,直接返回完整的 event.outputs JSON 字符串
|
|
190
|
+
resolve({
|
|
191
|
+
content: [
|
|
192
|
+
{
|
|
193
|
+
type: "text",
|
|
194
|
+
text: JSON.stringify(event.outputs),
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
});
|
|
285
198
|
}
|
|
286
199
|
else {
|
|
287
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ Alarm search failed`);
|
|
288
|
-
logger.error(`[SEARCH_ALARM_TOOL] - status: ${event.status}`);
|
|
289
|
-
logger.error(`[SEARCH_ALARM_TOOL] - outputs:`, JSON.stringify(event.outputs || {}));
|
|
290
200
|
reject(new Error(`检索闹钟失败: ${event.status}`));
|
|
291
201
|
}
|
|
292
202
|
}
|
|
293
203
|
};
|
|
294
204
|
// Register event handler
|
|
295
|
-
logger.log(`[SEARCH_ALARM_TOOL] 📡 Registering data-event handler on WebSocket manager`);
|
|
296
205
|
wsManager.on("data-event", handler);
|
|
297
206
|
// Send the command
|
|
298
|
-
logger.log(`[SEARCH_ALARM_TOOL] 📤 Sending SearchAlarm command...`);
|
|
299
207
|
sendCommand({
|
|
300
208
|
config,
|
|
301
209
|
sessionId,
|
|
@@ -304,10 +212,8 @@ b. 使用该工具之前需获取当前真实时间`,
|
|
|
304
212
|
command,
|
|
305
213
|
})
|
|
306
214
|
.then(() => {
|
|
307
|
-
logger.log(`[SEARCH_ALARM_TOOL] ✅ Command sent successfully, waiting for response...`);
|
|
308
215
|
})
|
|
309
216
|
.catch((error) => {
|
|
310
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ Failed to send command:`, error);
|
|
311
217
|
clearTimeout(timeout);
|
|
312
218
|
wsManager.off("data-event", handler);
|
|
313
219
|
reject(error);
|
|
@@ -327,17 +233,14 @@ function parseAlarmTimeToTimestamp(alarmTime) {
|
|
|
327
233
|
const trimmed = alarmTime.trim();
|
|
328
234
|
// Check basic format (should have at least 13 characters: YYYYMMDD hhmmss)
|
|
329
235
|
if (trimmed.length < 13) {
|
|
330
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ alarmTime too short: ${trimmed}`);
|
|
331
236
|
return null;
|
|
332
237
|
}
|
|
333
238
|
// Extract date and time parts
|
|
334
239
|
// Format: YYYYMMDD hhmmss
|
|
335
240
|
const datePart = trimmed.substring(0, 8); // YYYYMMDD
|
|
336
241
|
const timePart = trimmed.substring(8).trim(); // hhmmss (may have leading space)
|
|
337
|
-
logger.log(`[SEARCH_ALARM_TOOL] - datePart: ${datePart}, timePart: ${timePart}`);
|
|
338
242
|
// Validate lengths
|
|
339
243
|
if (datePart.length !== 8 || timePart.length !== 6) {
|
|
340
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ Invalid part lengths: datePart=${datePart.length}, timePart=${timePart.length}`);
|
|
341
244
|
return null;
|
|
342
245
|
}
|
|
343
246
|
// Parse components
|
|
@@ -347,45 +250,36 @@ function parseAlarmTimeToTimestamp(alarmTime) {
|
|
|
347
250
|
const hour = parseInt(timePart.substring(0, 2), 10);
|
|
348
251
|
const minute = parseInt(timePart.substring(2, 4), 10);
|
|
349
252
|
const second = parseInt(timePart.substring(4, 6), 10);
|
|
350
|
-
logger.log(`[SEARCH_ALARM_TOOL] - Parsed: ${year}-${month}-${day} ${hour}:${minute}:${second}`);
|
|
351
253
|
// Validate values
|
|
352
254
|
if (isNaN(year) || isNaN(month) || isNaN(day) ||
|
|
353
255
|
isNaN(hour) || isNaN(minute) || isNaN(second)) {
|
|
354
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ NaN detected in parsed values`);
|
|
355
256
|
return null;
|
|
356
257
|
}
|
|
357
258
|
// Validate ranges
|
|
358
259
|
if (month < 1 || month > 12) {
|
|
359
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ Invalid month: ${month}`);
|
|
360
260
|
return null;
|
|
361
261
|
}
|
|
362
262
|
if (day < 1 || day > 31) {
|
|
363
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ Invalid day: ${day}`);
|
|
364
263
|
return null;
|
|
365
264
|
}
|
|
366
265
|
if (hour < 0 || hour > 23) {
|
|
367
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ Invalid hour: ${hour}`);
|
|
368
266
|
return null;
|
|
369
267
|
}
|
|
370
268
|
if (minute < 0 || minute > 59) {
|
|
371
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ Invalid minute: ${minute}`);
|
|
372
269
|
return null;
|
|
373
270
|
}
|
|
374
271
|
if (second < 0 || second > 59) {
|
|
375
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ Invalid second: ${second}`);
|
|
376
272
|
return null;
|
|
377
273
|
}
|
|
378
274
|
// Create Date object and get timestamp
|
|
379
275
|
const date = new Date(year, month - 1, day, hour, minute, second);
|
|
380
276
|
const timestamp = date.getTime();
|
|
381
277
|
if (isNaN(timestamp)) {
|
|
382
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ Generated timestamp is NaN`);
|
|
383
278
|
return null;
|
|
384
279
|
}
|
|
385
280
|
return timestamp;
|
|
386
281
|
}
|
|
387
282
|
catch (error) {
|
|
388
|
-
logger.error(`[SEARCH_ALARM_TOOL] ❌ Exception in parseAlarmTimeToTimestamp:`, error);
|
|
389
283
|
return null;
|
|
390
284
|
}
|
|
391
285
|
}
|
|
@@ -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 calendar event tool - searches calendar events on user's device.
|
|
7
6
|
* Returns matching events based on time range and optional title filter.
|
|
@@ -28,6 +27,8 @@ export const searchCalendarTool = {
|
|
|
28
27
|
注意:
|
|
29
28
|
a. 该工具执行时间较长(最多60秒),请勿重复调用,超时或失败时最多重试一次。
|
|
30
29
|
b. 使用该工具之前需获取当前真实时间
|
|
30
|
+
|
|
31
|
+
回复约束:如果工具返回没有授权或者其他报错,只需要完整描述没有授权或者其他报错内容即可,不需要主动给用户提供解决方案,例如告诉用户如何授权,如何解决报错等都是不需要的,请严格遵守。
|
|
31
32
|
`,
|
|
32
33
|
parameters: {
|
|
33
34
|
type: "object",
|
|
@@ -48,19 +49,11 @@ b. 使用该工具之前需获取当前真实时间
|
|
|
48
49
|
required: ["startTime", "endTime"],
|
|
49
50
|
},
|
|
50
51
|
async execute(toolCallId, params) {
|
|
51
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] 🚀 Starting execution`);
|
|
52
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] - toolCallId: ${toolCallId}`);
|
|
53
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] - params:`, JSON.stringify(params));
|
|
54
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] - timestamp: ${new Date().toISOString()}`);
|
|
55
52
|
// Validate parameters
|
|
56
53
|
if (!params.startTime || !params.endTime) {
|
|
57
|
-
logger.error(`[SEARCH_CALENDAR_TOOL] ❌ Missing required parameters`);
|
|
58
54
|
throw new Error("Missing required parameters: startTime and endTime are required");
|
|
59
55
|
}
|
|
60
56
|
// Convert time strings to millisecond timestamps
|
|
61
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] 🕒 Converting time strings to timestamps...`);
|
|
62
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] - startTime input: ${params.startTime}`);
|
|
63
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] - endTime input: ${params.endTime}`);
|
|
64
57
|
// Parse YYYYMMDD hhmmss format
|
|
65
58
|
const parseTimeString = (timeStr) => {
|
|
66
59
|
// Remove any extra spaces and split
|
|
@@ -83,17 +76,6 @@ b. 使用该工具之前需获取当前真实时间
|
|
|
83
76
|
const date = new Date(year, month, day, hours, minutes, seconds);
|
|
84
77
|
return date.getTime();
|
|
85
78
|
};
|
|
86
|
-
// Helper function to convert timestamp to YYYYMMDD hhmmss format
|
|
87
|
-
const formatTimestamp = (timestamp) => {
|
|
88
|
-
const date = new Date(timestamp);
|
|
89
|
-
const year = date.getFullYear();
|
|
90
|
-
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
91
|
-
const day = String(date.getDate()).padStart(2, '0');
|
|
92
|
-
const hours = String(date.getHours()).padStart(2, '0');
|
|
93
|
-
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
94
|
-
const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
95
|
-
return `${year}${month}${day} ${hours}${minutes}${seconds}`;
|
|
96
|
-
};
|
|
97
79
|
let startTimeMs;
|
|
98
80
|
let endTimeMs;
|
|
99
81
|
try {
|
|
@@ -101,42 +83,26 @@ b. 使用该工具之前需获取当前真实时间
|
|
|
101
83
|
endTimeMs = parseTimeString(params.endTime);
|
|
102
84
|
}
|
|
103
85
|
catch (error) {
|
|
104
|
-
logger.error(`[SEARCH_CALENDAR_TOOL] ❌ Time parsing error:`, error);
|
|
105
86
|
throw new Error(`Invalid time format. Required format: YYYYMMDD hhmmss (e.g., 20240115 143000). Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
106
87
|
}
|
|
107
88
|
if (isNaN(startTimeMs) || isNaN(endTimeMs)) {
|
|
108
|
-
logger.error(`[SEARCH_CALENDAR_TOOL] ❌ Invalid time format`);
|
|
109
89
|
throw new Error("Invalid time format. Required format: YYYYMMDD hhmmss (e.g., 20240115 143000)");
|
|
110
90
|
}
|
|
111
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] ✅ Time conversion successful`);
|
|
112
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] - startTime timestamp: ${startTimeMs}`);
|
|
113
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] - endTime timestamp: ${endTimeMs}`);
|
|
114
91
|
// Get session context
|
|
115
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] 🔍 Attempting to get session context...`);
|
|
116
92
|
const sessionContext = getCurrentSessionContext();
|
|
117
93
|
if (!sessionContext) {
|
|
118
|
-
logger.error(`[SEARCH_CALENDAR_TOOL] ❌ FAILED: No active session found!`);
|
|
119
|
-
logger.error(`[SEARCH_CALENDAR_TOOL] - toolCallId: ${toolCallId}`);
|
|
120
94
|
throw new Error("No active XY session found. Search calendar tool can only be used during an active conversation.");
|
|
121
95
|
}
|
|
122
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] ✅ Session context found`);
|
|
123
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] - sessionId: ${sessionContext.sessionId}`);
|
|
124
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] - taskId: ${sessionContext.taskId}`);
|
|
125
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] - messageId: ${sessionContext.messageId}`);
|
|
126
96
|
const { config, sessionId, taskId, messageId } = sessionContext;
|
|
127
97
|
// Get WebSocket manager
|
|
128
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] 🔌 Getting WebSocket manager...`);
|
|
129
98
|
const wsManager = getXYWebSocketManager(config);
|
|
130
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] ✅ WebSocket manager obtained`);
|
|
131
99
|
// Build SearchCalendarEvent command
|
|
132
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] 📦 Building SearchCalendarEvent command...`);
|
|
133
100
|
// Build intentParam with timeInterval and optional title
|
|
134
101
|
const intentParam = {
|
|
135
102
|
timeInterval: [startTimeMs, endTimeMs],
|
|
136
103
|
};
|
|
137
104
|
if (params.title) {
|
|
138
105
|
intentParam.title = params.title;
|
|
139
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] - Including title filter: ${params.title}`);
|
|
140
106
|
}
|
|
141
107
|
const command = {
|
|
142
108
|
header: {
|
|
@@ -171,76 +137,35 @@ b. 使用该工具之前需获取当前真实时间
|
|
|
171
137
|
},
|
|
172
138
|
};
|
|
173
139
|
// Send command and wait for response (60 second timeout)
|
|
174
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] ⏳ Setting up promise to wait for calendar search response...`);
|
|
175
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] - Timeout: 60 seconds`);
|
|
176
140
|
return new Promise((resolve, reject) => {
|
|
177
141
|
const timeout = setTimeout(() => {
|
|
178
|
-
logger.error(`[SEARCH_CALENDAR_TOOL] ⏰ Timeout: No response received within 60 seconds`);
|
|
179
142
|
wsManager.off("data-event", handler);
|
|
180
143
|
reject(new Error("检索日程超时(60秒)"));
|
|
181
144
|
}, 60000);
|
|
182
145
|
// Listen for data events from WebSocket
|
|
183
146
|
const handler = (event) => {
|
|
184
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] 📨 Received data event:`, JSON.stringify(event));
|
|
185
147
|
if (event.intentName === "SearchCalendarEvent") {
|
|
186
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] 🎯 SearchCalendarEvent event received`);
|
|
187
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] - status: ${event.status}`);
|
|
188
148
|
clearTimeout(timeout);
|
|
189
149
|
wsManager.off("data-event", handler);
|
|
190
150
|
if (event.status === "success" && event.outputs) {
|
|
191
|
-
|
|
192
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] - outputs:`, JSON.stringify(event.outputs));
|
|
193
|
-
// Check for error code in outputs
|
|
194
|
-
if (event.outputs.retErrCode && event.outputs.retErrCode !== "0") {
|
|
195
|
-
logger.error(`[SEARCH_CALENDAR_TOOL] ❌ Device returned error`);
|
|
196
|
-
logger.error(`[SEARCH_CALENDAR_TOOL] - retErrCode: ${event.outputs.retErrCode}`);
|
|
197
|
-
logger.error(`[SEARCH_CALENDAR_TOOL] - errMsg: ${event.outputs.errMsg || "Unknown error"}`);
|
|
198
|
-
reject(new Error(`检索日程失败: ${event.outputs.errMsg || "未知错误"} (错误代码: ${event.outputs.retErrCode})`));
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
// Return the result directly as requested
|
|
202
|
-
const result = event.outputs.result;
|
|
203
|
-
// Ensure result is not undefined
|
|
204
|
-
if (result === undefined) {
|
|
205
|
-
logger.warn(`[SEARCH_CALENDAR_TOOL] ⚠️ Result is undefined, returning empty result`);
|
|
206
|
-
}
|
|
207
|
-
// Convert dtStart and dtEnd from timestamps to YYYYMMDD hhmmss format
|
|
208
|
-
if (result && result.items && Array.isArray(result.items)) {
|
|
209
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] 🔄 Converting timestamps to formatted dates...`);
|
|
210
|
-
result.items = result.items.map((item) => {
|
|
211
|
-
const formattedItem = { ...item };
|
|
212
|
-
if (item.dtStart) {
|
|
213
|
-
formattedItem.dtStart = formatTimestamp(item.dtStart);
|
|
214
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] - dtStart: ${item.dtStart} -> ${formattedItem.dtStart}`);
|
|
215
|
-
}
|
|
216
|
-
if (item.dtEnd) {
|
|
217
|
-
formattedItem.dtEnd = formatTimestamp(item.dtEnd);
|
|
218
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] - dtEnd: ${item.dtEnd} -> ${formattedItem.dtEnd}`);
|
|
219
|
-
}
|
|
220
|
-
return formattedItem;
|
|
221
|
-
});
|
|
222
|
-
}
|
|
151
|
+
// 成功,直接返回完整的 event.outputs JSON 字符串
|
|
223
152
|
resolve({
|
|
224
153
|
content: [
|
|
225
154
|
{
|
|
226
155
|
type: "text",
|
|
227
|
-
text:
|
|
156
|
+
text: JSON.stringify(event.outputs),
|
|
228
157
|
},
|
|
229
158
|
],
|
|
230
159
|
});
|
|
231
160
|
}
|
|
232
161
|
else {
|
|
233
|
-
logger.error(`[SEARCH_CALENDAR_TOOL] ❌ Calendar event search failed`);
|
|
234
|
-
logger.error(`[SEARCH_CALENDAR_TOOL] - status: ${event.status}`);
|
|
235
162
|
reject(new Error(`检索日程失败: ${event.status}`));
|
|
236
163
|
}
|
|
237
164
|
}
|
|
238
165
|
};
|
|
239
166
|
// Register event handler
|
|
240
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] 📡 Registering data-event handler on WebSocket manager`);
|
|
241
167
|
wsManager.on("data-event", handler);
|
|
242
168
|
// Send the command
|
|
243
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] 📤 Sending SearchCalendarEvent command...`);
|
|
244
169
|
sendCommand({
|
|
245
170
|
config,
|
|
246
171
|
sessionId,
|
|
@@ -249,10 +174,8 @@ b. 使用该工具之前需获取当前真实时间
|
|
|
249
174
|
command,
|
|
250
175
|
})
|
|
251
176
|
.then(() => {
|
|
252
|
-
logger.log(`[SEARCH_CALENDAR_TOOL] ✅ Command sent successfully, waiting for response...`);
|
|
253
177
|
})
|
|
254
178
|
.catch((error) => {
|
|
255
|
-
logger.error(`[SEARCH_CALENDAR_TOOL] ❌ Failed to send command:`, error);
|
|
256
179
|
clearTimeout(timeout);
|
|
257
180
|
wsManager.off("data-event", handler);
|
|
258
181
|
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 contact tool - searches contacts on user's device.
|
|
7
6
|
* Returns matching contact information based on name.
|
|
@@ -21,34 +20,19 @@ export const searchContactTool = {
|
|
|
21
20
|
required: ["name"],
|
|
22
21
|
},
|
|
23
22
|
async execute(toolCallId, params) {
|
|
24
|
-
logger.log(`[SEARCH_CONTACT_TOOL] 🚀 Starting execution`);
|
|
25
|
-
logger.log(`[SEARCH_CONTACT_TOOL] - toolCallId: ${toolCallId}`);
|
|
26
|
-
logger.log(`[SEARCH_CONTACT_TOOL] - params:`, JSON.stringify(params));
|
|
27
|
-
logger.log(`[SEARCH_CONTACT_TOOL] - timestamp: ${new Date().toISOString()}`);
|
|
28
23
|
// Validate parameters
|
|
29
24
|
if (!params.name) {
|
|
30
|
-
logger.error(`[SEARCH_CONTACT_TOOL] ❌ Missing required parameter: name`);
|
|
31
25
|
throw new Error("Missing required parameter: name is required");
|
|
32
26
|
}
|
|
33
27
|
// Get session context
|
|
34
|
-
logger.log(`[SEARCH_CONTACT_TOOL] 🔍 Attempting to get session context...`);
|
|
35
28
|
const sessionContext = getCurrentSessionContext();
|
|
36
29
|
if (!sessionContext) {
|
|
37
|
-
logger.error(`[SEARCH_CONTACT_TOOL] ❌ FAILED: No active session found!`);
|
|
38
|
-
logger.error(`[SEARCH_CONTACT_TOOL] - toolCallId: ${toolCallId}`);
|
|
39
30
|
throw new Error("No active XY session found. Search contact tool can only be used during an active conversation.");
|
|
40
31
|
}
|
|
41
|
-
logger.log(`[SEARCH_CONTACT_TOOL] ✅ Session context found`);
|
|
42
|
-
logger.log(`[SEARCH_CONTACT_TOOL] - sessionId: ${sessionContext.sessionId}`);
|
|
43
|
-
logger.log(`[SEARCH_CONTACT_TOOL] - taskId: ${sessionContext.taskId}`);
|
|
44
|
-
logger.log(`[SEARCH_CONTACT_TOOL] - messageId: ${sessionContext.messageId}`);
|
|
45
32
|
const { config, sessionId, taskId, messageId } = sessionContext;
|
|
46
33
|
// Get WebSocket manager
|
|
47
|
-
logger.log(`[SEARCH_CONTACT_TOOL] 🔌 Getting WebSocket manager...`);
|
|
48
34
|
const wsManager = getXYWebSocketManager(config);
|
|
49
|
-
logger.log(`[SEARCH_CONTACT_TOOL] ✅ WebSocket manager obtained`);
|
|
50
35
|
// Build SearchContactLocal command
|
|
51
|
-
logger.log(`[SEARCH_CONTACT_TOOL] 📦 Building SearchContactLocal command...`);
|
|
52
36
|
const command = {
|
|
53
37
|
header: {
|
|
54
38
|
namespace: "Common",
|
|
@@ -83,70 +67,35 @@ export const searchContactTool = {
|
|
|
83
67
|
},
|
|
84
68
|
};
|
|
85
69
|
// Send command and wait for response (60 second timeout)
|
|
86
|
-
logger.log(`[SEARCH_CONTACT_TOOL] ⏳ Setting up promise to wait for contact search response...`);
|
|
87
|
-
logger.log(`[SEARCH_CONTACT_TOOL] - Timeout: 60 seconds`);
|
|
88
70
|
return new Promise((resolve, reject) => {
|
|
89
71
|
const timeout = setTimeout(() => {
|
|
90
|
-
logger.error(`[SEARCH_CONTACT_TOOL] ⏰ Timeout: No response received within 60 seconds`);
|
|
91
72
|
wsManager.off("data-event", handler);
|
|
92
73
|
reject(new Error("搜索联系人超时(60秒)"));
|
|
93
74
|
}, 60000);
|
|
94
75
|
// Listen for data events from WebSocket
|
|
95
76
|
const handler = (event) => {
|
|
96
|
-
logger.log(`[SEARCH_CONTACT_TOOL] 📨 Received data event:`, JSON.stringify(event));
|
|
97
77
|
if (event.intentName === "SearchContactLocal") {
|
|
98
|
-
logger.log(`[SEARCH_CONTACT_TOOL] 🎯 SearchContactLocal event received`);
|
|
99
|
-
logger.log(`[SEARCH_CONTACT_TOOL] - status: ${event.status}`);
|
|
100
78
|
clearTimeout(timeout);
|
|
101
79
|
wsManager.off("data-event", handler);
|
|
102
80
|
if (event.status === "success" && event.outputs) {
|
|
103
|
-
|
|
104
|
-
logger.log(`[SEARCH_CONTACT_TOOL] - outputs:`, JSON.stringify(event.outputs));
|
|
105
|
-
// Check for error code first
|
|
106
|
-
if (event.outputs.retErrCode && event.outputs.retErrCode !== "0") {
|
|
107
|
-
logger.error(`[SEARCH_CONTACT_TOOL] ❌ Search failed with error code: ${event.outputs.retErrCode}`);
|
|
108
|
-
logger.error(`[SEARCH_CONTACT_TOOL] - errMsg: ${event.outputs.errMsg}`);
|
|
109
|
-
reject(new Error(`搜索联系人失败: ${event.outputs.errMsg || '未知错误'} (错误码: ${event.outputs.retErrCode})`));
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
// Get the result
|
|
113
|
-
const result = event.outputs.result;
|
|
114
|
-
// Check if result exists
|
|
115
|
-
if (!result) {
|
|
116
|
-
logger.warn(`[SEARCH_CONTACT_TOOL] ⚠️ No result found for name "${params.name}"`);
|
|
117
|
-
resolve({
|
|
118
|
-
content: [
|
|
119
|
-
{
|
|
120
|
-
type: "text",
|
|
121
|
-
text: JSON.stringify({ items: [], message: "未找到匹配的联系人" }),
|
|
122
|
-
},
|
|
123
|
-
],
|
|
124
|
-
});
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
logger.log(`[SEARCH_CONTACT_TOOL] 📊 Contacts found: ${result?.items?.length || 0} results for name "${params.name}"`);
|
|
128
|
-
// Return the result with valid string content
|
|
81
|
+
// 成功,直接返回完整的 event.outputs JSON 字符串
|
|
129
82
|
resolve({
|
|
130
83
|
content: [
|
|
131
84
|
{
|
|
132
85
|
type: "text",
|
|
133
|
-
text: JSON.stringify(
|
|
86
|
+
text: JSON.stringify(event.outputs),
|
|
134
87
|
},
|
|
135
88
|
],
|
|
136
89
|
});
|
|
137
90
|
}
|
|
138
91
|
else {
|
|
139
|
-
logger.error(`[SEARCH_CONTACT_TOOL] ❌ Contact search failed`);
|
|
140
|
-
logger.error(`[SEARCH_CONTACT_TOOL] - status: ${event.status}`);
|
|
141
92
|
reject(new Error(`搜索联系人失败: ${event.status}`));
|
|
142
93
|
}
|
|
143
94
|
}
|
|
144
95
|
};
|
|
145
96
|
// Register event handler
|
|
146
|
-
logger.log(`[SEARCH_CONTACT_TOOL] 📡 Registering data-event handler on WebSocket manager`);
|
|
147
97
|
wsManager.on("data-event", handler);
|
|
148
98
|
// Send the command
|
|
149
|
-
logger.log(`[SEARCH_CONTACT_TOOL] 📤 Sending SearchContactLocal command...`);
|
|
150
99
|
sendCommand({
|
|
151
100
|
config,
|
|
152
101
|
sessionId,
|
|
@@ -155,10 +104,8 @@ export const searchContactTool = {
|
|
|
155
104
|
command,
|
|
156
105
|
})
|
|
157
106
|
.then(() => {
|
|
158
|
-
logger.log(`[SEARCH_CONTACT_TOOL] ✅ Command sent successfully, waiting for response...`);
|
|
159
107
|
})
|
|
160
108
|
.catch((error) => {
|
|
161
|
-
logger.error(`[SEARCH_CONTACT_TOOL] ❌ Failed to send command:`, error);
|
|
162
109
|
clearTimeout(timeout);
|
|
163
110
|
wsManager.off("data-event", handler);
|
|
164
111
|
reject(error);
|