@ynhcj/xiaoyi-channel 0.0.50-beta → 0.0.52-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +42 -0
- package/dist/src/bot.js +3 -16
- package/dist/src/client.js +0 -3
- package/dist/src/cspl/call-api.d.ts +3 -0
- package/dist/src/cspl/call-api.js +79 -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/formatter.js +3 -23
- package/dist/src/heartbeat.js +0 -4
- package/dist/src/reply-dispatcher.js +32 -0
- package/dist/src/steer-injector.d.ts +16 -0
- package/dist/src/steer-injector.js +74 -0
- package/dist/src/tools/calendar-tool.js +2 -37
- package/dist/src/tools/call-phone-tool.js +1 -42
- package/dist/src/tools/create-alarm-tool.js +3 -74
- package/dist/src/tools/delete-alarm-tool.js +3 -45
- package/dist/src/tools/image-reading-tool.js +0 -47
- package/dist/src/tools/location-tool.js +1 -32
- package/dist/src/tools/modify-alarm-tool.js +3 -77
- package/dist/src/tools/modify-note-tool.js +1 -34
- package/dist/src/tools/note-tool.js +2 -4
- package/dist/src/tools/search-alarm-tool.js +3 -61
- package/dist/src/tools/search-calendar-tool.js +2 -39
- package/dist/src/tools/search-contact-tool.js +0 -30
- package/dist/src/tools/search-file-tool.js +0 -33
- package/dist/src/tools/search-message-tool.js +0 -33
- package/dist/src/tools/search-note-tool.js +1 -26
- package/dist/src/tools/search-photo-gallery-tool.js +2 -31
- package/dist/src/tools/send-file-to-user-tool.js +0 -39
- package/dist/src/tools/send-message-tool.js +1 -39
- package/dist/src/tools/session-manager.js +0 -45
- 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/websocket.js +24 -10
- package/package.json +1 -1
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { getXYWebSocketManager } from "../client.js";
|
|
2
2
|
import { sendCommand } from "../formatter.js";
|
|
3
3
|
import { getCurrentSessionContext } from "./session-manager.js";
|
|
4
|
-
import { logger } from "../utils/logger.js";
|
|
5
4
|
/**
|
|
6
5
|
* XY calendar event tool - creates a calendar event on user's device.
|
|
7
6
|
* Requires title, dtStart (start time), and dtEnd (end time) parameters.
|
|
@@ -12,6 +11,8 @@ export const calendarTool = {
|
|
|
12
11
|
label: "Create Calendar Event",
|
|
13
12
|
description: `在用户设备上创建日程。需要提供日程标题、开始时间和结束时间。时间格式必须为:yyyy-mm-dd hh:mm:ss(例如:2024-01-15 14:30:00)。注意:该工具执行时间较长(最多60秒),请勿重复调用,超时或失败时最多重试一次。
|
|
14
13
|
注意事项:使用该工具之前需获取当前真实时间
|
|
14
|
+
|
|
15
|
+
回复约束:如果工具返回没有授权或者其他报错,只需要完整描述没有授权或者其他报错内容即可,不需要主动给用户提供解决方案,例如告诉用户如何授权,如何解决报错等都是不需要的,请严格遵守。
|
|
15
16
|
`,
|
|
16
17
|
parameters: {
|
|
17
18
|
type: "object",
|
|
@@ -32,47 +33,25 @@ export const calendarTool = {
|
|
|
32
33
|
required: ["title", "dtStart", "dtEnd"],
|
|
33
34
|
},
|
|
34
35
|
async execute(toolCallId, params) {
|
|
35
|
-
logger.log(`[CALENDAR_TOOL] 🚀 Starting execution`);
|
|
36
|
-
logger.log(`[CALENDAR_TOOL] - toolCallId: ${toolCallId}`);
|
|
37
|
-
logger.log(`[CALENDAR_TOOL] - params:`, JSON.stringify(params));
|
|
38
|
-
logger.log(`[CALENDAR_TOOL] - timestamp: ${new Date().toISOString()}`);
|
|
39
36
|
// Validate parameters
|
|
40
37
|
if (!params.title || !params.dtStart || !params.dtEnd) {
|
|
41
|
-
logger.error(`[CALENDAR_TOOL] ❌ Missing required parameters`);
|
|
42
38
|
throw new Error("Missing required parameters: title, dtStart, and dtEnd are required");
|
|
43
39
|
}
|
|
44
40
|
// Convert time strings to millisecond timestamps
|
|
45
|
-
logger.log(`[CALENDAR_TOOL] 🕒 Converting time strings to timestamps...`);
|
|
46
|
-
logger.log(`[CALENDAR_TOOL] - dtStart input: ${params.dtStart}`);
|
|
47
|
-
logger.log(`[CALENDAR_TOOL] - dtEnd input: ${params.dtEnd}`);
|
|
48
41
|
const dtStartMs = new Date(params.dtStart).getTime();
|
|
49
42
|
const dtEndMs = new Date(params.dtEnd).getTime();
|
|
50
43
|
if (isNaN(dtStartMs) || isNaN(dtEndMs)) {
|
|
51
|
-
logger.error(`[CALENDAR_TOOL] ❌ Invalid time format`);
|
|
52
44
|
throw new Error("Invalid time format. Required format: yyyy-mm-dd hh:mm:ss (e.g., 2024-01-15 14:30:00)");
|
|
53
45
|
}
|
|
54
|
-
logger.log(`[CALENDAR_TOOL] ✅ Time conversion successful`);
|
|
55
|
-
logger.log(`[CALENDAR_TOOL] - dtStart timestamp: ${dtStartMs}`);
|
|
56
|
-
logger.log(`[CALENDAR_TOOL] - dtEnd timestamp: ${dtEndMs}`);
|
|
57
46
|
// Get session context
|
|
58
|
-
logger.log(`[CALENDAR_TOOL] 🔍 Attempting to get session context...`);
|
|
59
47
|
const sessionContext = getCurrentSessionContext();
|
|
60
48
|
if (!sessionContext) {
|
|
61
|
-
logger.error(`[CALENDAR_TOOL] ❌ FAILED: No active session found!`);
|
|
62
|
-
logger.error(`[CALENDAR_TOOL] - toolCallId: ${toolCallId}`);
|
|
63
49
|
throw new Error("No active XY session found. Calendar tool can only be used during an active conversation.");
|
|
64
50
|
}
|
|
65
|
-
logger.log(`[CALENDAR_TOOL] ✅ Session context found`);
|
|
66
|
-
logger.log(`[CALENDAR_TOOL] - sessionId: ${sessionContext.sessionId}`);
|
|
67
|
-
logger.log(`[CALENDAR_TOOL] - taskId: ${sessionContext.taskId}`);
|
|
68
|
-
logger.log(`[CALENDAR_TOOL] - messageId: ${sessionContext.messageId}`);
|
|
69
51
|
const { config, sessionId, taskId, messageId } = sessionContext;
|
|
70
52
|
// Get WebSocket manager
|
|
71
|
-
logger.log(`[CALENDAR_TOOL] 🔌 Getting WebSocket manager...`);
|
|
72
53
|
const wsManager = getXYWebSocketManager(config);
|
|
73
|
-
logger.log(`[CALENDAR_TOOL] ✅ WebSocket manager obtained`);
|
|
74
54
|
// Build CreateCalendarEvent command
|
|
75
|
-
logger.log(`[CALENDAR_TOOL] 📦 Building CreateCalendarEvent command...`);
|
|
76
55
|
const command = {
|
|
77
56
|
header: {
|
|
78
57
|
namespace: "Common",
|
|
@@ -108,25 +87,17 @@ export const calendarTool = {
|
|
|
108
87
|
},
|
|
109
88
|
};
|
|
110
89
|
// Send command and wait for response (60 second timeout)
|
|
111
|
-
logger.log(`[CALENDAR_TOOL] ⏳ Setting up promise to wait for calendar event response...`);
|
|
112
|
-
logger.log(`[CALENDAR_TOOL] - Timeout: 60 seconds`);
|
|
113
90
|
return new Promise((resolve, reject) => {
|
|
114
91
|
const timeout = setTimeout(() => {
|
|
115
|
-
logger.error(`[CALENDAR_TOOL] ⏰ Timeout: No response received within 60 seconds`);
|
|
116
92
|
wsManager.off("data-event", handler);
|
|
117
93
|
reject(new Error("创建日程超时(60秒)"));
|
|
118
94
|
}, 60000);
|
|
119
95
|
// Listen for data events from WebSocket
|
|
120
96
|
const handler = (event) => {
|
|
121
|
-
logger.log(`[CALENDAR_TOOL] 📨 Received data event:`, JSON.stringify(event));
|
|
122
97
|
if (event.intentName === "CreateCalendarEvent") {
|
|
123
|
-
logger.log(`[CALENDAR_TOOL] 🎯 CreateCalendarEvent event received`);
|
|
124
|
-
logger.log(`[CALENDAR_TOOL] - status: ${event.status}`);
|
|
125
98
|
clearTimeout(timeout);
|
|
126
99
|
wsManager.off("data-event", handler);
|
|
127
100
|
if (event.status === "success" && event.outputs) {
|
|
128
|
-
logger.log(`[CALENDAR_TOOL] ✅ Calendar event created successfully`);
|
|
129
|
-
logger.log(`[CALENDAR_TOOL] - outputs:`, JSON.stringify(event.outputs));
|
|
130
101
|
resolve({
|
|
131
102
|
content: [
|
|
132
103
|
{
|
|
@@ -137,17 +108,13 @@ export const calendarTool = {
|
|
|
137
108
|
});
|
|
138
109
|
}
|
|
139
110
|
else {
|
|
140
|
-
logger.error(`[CALENDAR_TOOL] ❌ Calendar event creation failed`);
|
|
141
|
-
logger.error(`[CALENDAR_TOOL] - status: ${event.status}`);
|
|
142
111
|
reject(new Error(`创建日程失败: ${event.status}`));
|
|
143
112
|
}
|
|
144
113
|
}
|
|
145
114
|
};
|
|
146
115
|
// Register event handler
|
|
147
|
-
logger.log(`[CALENDAR_TOOL] 📡 Registering data-event handler on WebSocket manager`);
|
|
148
116
|
wsManager.on("data-event", handler);
|
|
149
117
|
// Send the command
|
|
150
|
-
logger.log(`[CALENDAR_TOOL] 📤 Sending CreateCalendarEvent command...`);
|
|
151
118
|
sendCommand({
|
|
152
119
|
config,
|
|
153
120
|
sessionId,
|
|
@@ -156,10 +123,8 @@ export const calendarTool = {
|
|
|
156
123
|
command,
|
|
157
124
|
})
|
|
158
125
|
.then(() => {
|
|
159
|
-
logger.log(`[CALENDAR_TOOL] ✅ Command sent successfully, waiting for response...`);
|
|
160
126
|
})
|
|
161
127
|
.catch((error) => {
|
|
162
|
-
logger.error(`[CALENDAR_TOOL] ❌ Failed to send command:`, error);
|
|
163
128
|
clearTimeout(timeout);
|
|
164
129
|
wsManager.off("data-event", handler);
|
|
165
130
|
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 call phone tool - makes a phone call on user's device.
|
|
7
6
|
* Requires phoneNumber parameter and optional slotId (0 for primary SIM, 1 for secondary SIM).
|
|
@@ -9,7 +8,7 @@ import { logger } from "../utils/logger.js";
|
|
|
9
8
|
export const callPhoneTool = {
|
|
10
9
|
name: "call_phone",
|
|
11
10
|
label: "Call Phone",
|
|
12
|
-
description: "拨打电话。需要提供要拨打的电话号码。slotId参数可选,默认为0(主卡),如果用户明确要求使用副卡则设置为1。注意:操作超时时间为60
|
|
11
|
+
description: "拨打电话。需要提供要拨打的电话号码。slotId参数可选,默认为0(主卡),如果用户明确要求使用副卡则设置为1。注意:操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。回复约束:如果工具返回没有授权或者其他报错,只需要完整描述没有授权或者其他报错内容即可,不需要主动给用户提供解决方案,例如告诉用户如何授权,如何解决报错等都是不需要的,请严格遵守。",
|
|
13
12
|
parameters: {
|
|
14
13
|
type: "object",
|
|
15
14
|
properties: {
|
|
@@ -26,43 +25,25 @@ export const callPhoneTool = {
|
|
|
26
25
|
required: ["phoneNumber"],
|
|
27
26
|
},
|
|
28
27
|
async execute(toolCallId, params) {
|
|
29
|
-
logger.log(`[CALL_PHONE_TOOL] 🚀 Starting execution`);
|
|
30
|
-
logger.log(`[CALL_PHONE_TOOL] - toolCallId: ${toolCallId}`);
|
|
31
|
-
logger.log(`[CALL_PHONE_TOOL] - params:`, JSON.stringify(params));
|
|
32
|
-
logger.log(`[CALL_PHONE_TOOL] - timestamp: ${new Date().toISOString()}`);
|
|
33
28
|
// Validate phoneNumber parameter
|
|
34
29
|
if (!params.phoneNumber || typeof params.phoneNumber !== "string" || params.phoneNumber.trim() === "") {
|
|
35
|
-
logger.error(`[CALL_PHONE_TOOL] ❌ Missing or invalid phoneNumber parameter`);
|
|
36
30
|
throw new Error("Missing required parameter: phoneNumber must be a non-empty string");
|
|
37
31
|
}
|
|
38
32
|
// Set default slotId if not provided
|
|
39
33
|
const slotId = params.slotId !== undefined && params.slotId !== null ? params.slotId : 0;
|
|
40
34
|
// Validate slotId (must be 0 or 1)
|
|
41
35
|
if (slotId !== 0 && slotId !== 1) {
|
|
42
|
-
logger.error(`[CALL_PHONE_TOOL] ❌ Invalid slotId: ${slotId}`);
|
|
43
36
|
throw new Error("Invalid slotId: must be 0 (primary SIM) or 1 (secondary SIM)");
|
|
44
37
|
}
|
|
45
|
-
logger.log(`[CALL_PHONE_TOOL] 📞 Preparing to call phone number: ${params.phoneNumber}`);
|
|
46
|
-
logger.log(`[CALL_PHONE_TOOL] - slotId: ${slotId} (${slotId === 0 ? "主卡" : "副卡"})`);
|
|
47
38
|
// Get session context
|
|
48
|
-
logger.log(`[CALL_PHONE_TOOL] 🔍 Attempting to get session context...`);
|
|
49
39
|
const sessionContext = getCurrentSessionContext();
|
|
50
40
|
if (!sessionContext) {
|
|
51
|
-
logger.error(`[CALL_PHONE_TOOL] ❌ FAILED: No active session found!`);
|
|
52
|
-
logger.error(`[CALL_PHONE_TOOL] - toolCallId: ${toolCallId}`);
|
|
53
41
|
throw new Error("No active XY session found. Call phone tool can only be used during an active conversation.");
|
|
54
42
|
}
|
|
55
|
-
logger.log(`[CALL_PHONE_TOOL] ✅ Session context found`);
|
|
56
|
-
logger.log(`[CALL_PHONE_TOOL] - sessionId: ${sessionContext.sessionId}`);
|
|
57
|
-
logger.log(`[CALL_PHONE_TOOL] - taskId: ${sessionContext.taskId}`);
|
|
58
|
-
logger.log(`[CALL_PHONE_TOOL] - messageId: ${sessionContext.messageId}`);
|
|
59
43
|
const { config, sessionId, taskId, messageId } = sessionContext;
|
|
60
44
|
// Get WebSocket manager
|
|
61
|
-
logger.log(`[CALL_PHONE_TOOL] 🔌 Getting WebSocket manager...`);
|
|
62
45
|
const wsManager = getXYWebSocketManager(config);
|
|
63
|
-
logger.log(`[CALL_PHONE_TOOL] ✅ WebSocket manager obtained`);
|
|
64
46
|
// Build StartCall command
|
|
65
|
-
logger.log(`[CALL_PHONE_TOOL] 📦 Building StartCall command...`);
|
|
66
47
|
const command = {
|
|
67
48
|
header: {
|
|
68
49
|
namespace: "Common",
|
|
@@ -97,34 +78,22 @@ export const callPhoneTool = {
|
|
|
97
78
|
pageControlRelated: false,
|
|
98
79
|
},
|
|
99
80
|
};
|
|
100
|
-
logger.log(`[CALL_PHONE_TOOL] 📋 Command details:`, JSON.stringify(command, null, 2));
|
|
101
81
|
// Send command and wait for response (60 second timeout)
|
|
102
|
-
logger.log(`[CALL_PHONE_TOOL] ⏳ Setting up promise to wait for call response...`);
|
|
103
|
-
logger.log(`[CALL_PHONE_TOOL] - Timeout: 60 seconds`);
|
|
104
82
|
return new Promise((resolve, reject) => {
|
|
105
83
|
const timeout = setTimeout(() => {
|
|
106
|
-
logger.error(`[CALL_PHONE_TOOL] ⏰ Timeout: No response received within 60 seconds`);
|
|
107
84
|
wsManager.off("data-event", handler);
|
|
108
85
|
reject(new Error("拨打电话超时(60秒)"));
|
|
109
86
|
}, 60000);
|
|
110
87
|
// Listen for data events from WebSocket
|
|
111
88
|
const handler = (event) => {
|
|
112
|
-
logger.log(`[CALL_PHONE_TOOL] 📨 Received data event:`, JSON.stringify(event));
|
|
113
89
|
if (event.intentName === "StartCall") {
|
|
114
|
-
logger.log(`[CALL_PHONE_TOOL] 🎯 StartCall event received`);
|
|
115
|
-
logger.log(`[CALL_PHONE_TOOL] - status: ${event.status}`);
|
|
116
90
|
clearTimeout(timeout);
|
|
117
91
|
wsManager.off("data-event", handler);
|
|
118
92
|
if (event.status === "success" && event.outputs) {
|
|
119
|
-
logger.log(`[CALL_PHONE_TOOL] ✅ Call response received`);
|
|
120
|
-
logger.log(`[CALL_PHONE_TOOL] - outputs:`, JSON.stringify(event.outputs));
|
|
121
93
|
// Check for error code in outputs
|
|
122
94
|
const code = event.outputs.code !== undefined ? event.outputs.code : null;
|
|
123
95
|
if (code !== null && code !== 0) {
|
|
124
|
-
logger.error(`[CALL_PHONE_TOOL] ❌ Device returned error`);
|
|
125
|
-
logger.error(`[CALL_PHONE_TOOL] - code: ${code}`);
|
|
126
96
|
const errorMsg = event.outputs.errorMsg || event.outputs.errMsg || "未知错误";
|
|
127
|
-
logger.error(`[CALL_PHONE_TOOL] - errorMsg: ${errorMsg}`);
|
|
128
97
|
reject(new Error(`拨打电话失败: ${errorMsg} (错误代码: ${code})`));
|
|
129
98
|
return;
|
|
130
99
|
}
|
|
@@ -136,9 +105,6 @@ export const callPhoneTool = {
|
|
|
136
105
|
slotId: slotId,
|
|
137
106
|
message: "电话拨打成功",
|
|
138
107
|
};
|
|
139
|
-
logger.log(`[CALL_PHONE_TOOL] 🎉 Call initiated successfully`);
|
|
140
|
-
logger.log(`[CALL_PHONE_TOOL] - phoneNumber: ${params.phoneNumber}`);
|
|
141
|
-
logger.log(`[CALL_PHONE_TOOL] - slotId: ${slotId}`);
|
|
142
108
|
resolve({
|
|
143
109
|
content: [
|
|
144
110
|
{
|
|
@@ -149,19 +115,14 @@ export const callPhoneTool = {
|
|
|
149
115
|
});
|
|
150
116
|
}
|
|
151
117
|
else {
|
|
152
|
-
logger.error(`[CALL_PHONE_TOOL] ❌ Call failed`);
|
|
153
|
-
logger.error(`[CALL_PHONE_TOOL] - status: ${event.status}`);
|
|
154
|
-
logger.error(`[CALL_PHONE_TOOL] - outputs:`, JSON.stringify(event.outputs || {}));
|
|
155
118
|
const errorDetail = event.outputs ? JSON.stringify(event.outputs) : event.status;
|
|
156
119
|
reject(new Error(`拨打电话失败: ${errorDetail}`));
|
|
157
120
|
}
|
|
158
121
|
}
|
|
159
122
|
};
|
|
160
123
|
// Register event handler
|
|
161
|
-
logger.log(`[CALL_PHONE_TOOL] 📡 Registering data-event handler on WebSocket manager`);
|
|
162
124
|
wsManager.on("data-event", handler);
|
|
163
125
|
// Send the command
|
|
164
|
-
logger.log(`[CALL_PHONE_TOOL] 📤 Sending StartCall command...`);
|
|
165
126
|
sendCommand({
|
|
166
127
|
config,
|
|
167
128
|
sessionId,
|
|
@@ -170,10 +131,8 @@ export const callPhoneTool = {
|
|
|
170
131
|
command,
|
|
171
132
|
})
|
|
172
133
|
.then(() => {
|
|
173
|
-
logger.log(`[CALL_PHONE_TOOL] ✅ Command sent successfully, waiting for response...`);
|
|
174
134
|
})
|
|
175
135
|
.catch((error) => {
|
|
176
|
-
logger.error(`[CALL_PHONE_TOOL] ❌ Failed to send command:`, error);
|
|
177
136
|
clearTimeout(timeout);
|
|
178
137
|
wsManager.off("data-event", handler);
|
|
179
138
|
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
|
// Enum definitions for alarm parameters
|
|
6
5
|
const ALARM_SNOOZE_DURATION_VALUES = [5, 10, 15, 20, 25, 30];
|
|
7
6
|
const ALARM_SNOOZE_TOTAL_VALUES = [0, 1, 3, 5, 10];
|
|
@@ -32,7 +31,9 @@ export const createAlarmTool = {
|
|
|
32
31
|
|
|
33
32
|
注意事项:
|
|
34
33
|
a. 操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。
|
|
35
|
-
b.
|
|
34
|
+
b. 使用该工具之前需获取当前真实时间
|
|
35
|
+
|
|
36
|
+
回复约束:如果工具返回没有授权或者其他报错,只需要完整描述没有授权或者其他报错内容即可,不需要主动给用户提供解决方案,例如告诉用户如何授权,如何解决报错等都是不需要的,请严格遵守。`,
|
|
36
37
|
parameters: {
|
|
37
38
|
type: "object",
|
|
38
39
|
properties: {
|
|
@@ -69,170 +70,127 @@ b. 使用该工具之前需获取当前真实时间`,
|
|
|
69
70
|
required: ["alarmTime"],
|
|
70
71
|
},
|
|
71
72
|
async execute(toolCallId, params) {
|
|
72
|
-
logger.log(`[CREATE_ALARM_TOOL] 🚀 Starting execution`);
|
|
73
|
-
logger.log(`[CREATE_ALARM_TOOL] - toolCallId: ${toolCallId}`);
|
|
74
|
-
logger.log(`[CREATE_ALARM_TOOL] - params:`, JSON.stringify(params));
|
|
75
|
-
logger.log(`[CREATE_ALARM_TOOL] - timestamp: ${new Date().toISOString()}`);
|
|
76
73
|
// ===== Validate required parameter: alarmTime =====
|
|
77
74
|
if (!params.alarmTime || typeof params.alarmTime !== "string") {
|
|
78
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Missing or invalid alarmTime`);
|
|
79
75
|
throw new Error("Missing required parameter: alarmTime must be a string in format YYYYMMDD hhmmss");
|
|
80
76
|
}
|
|
81
77
|
// Parse and convert alarmTime to timestamp
|
|
82
|
-
logger.log(`[CREATE_ALARM_TOOL] 🕒 Parsing alarmTime: ${params.alarmTime}`);
|
|
83
78
|
const alarmTimeMs = parseAlarmTimeToTimestamp(params.alarmTime);
|
|
84
79
|
if (alarmTimeMs === null) {
|
|
85
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Invalid alarmTime format`);
|
|
86
80
|
throw new Error("Invalid alarmTime format. Required format: YYYYMMDD hhmmss (e.g., 20240315 143000)");
|
|
87
81
|
}
|
|
88
|
-
logger.log(`[CREATE_ALARM_TOOL] ✅ alarmTime converted to timestamp: ${alarmTimeMs}`);
|
|
89
82
|
// ===== Validate and set optional parameters with defaults =====
|
|
90
83
|
// alarmTitle - default to "闹钟"
|
|
91
84
|
const alarmTitle = params.alarmTitle && typeof params.alarmTitle === "string"
|
|
92
85
|
? params.alarmTitle
|
|
93
86
|
: "闹钟";
|
|
94
|
-
logger.log(`[CREATE_ALARM_TOOL] - alarmTitle: ${alarmTitle}`);
|
|
95
87
|
// alarmSnoozeDuration - default 10
|
|
96
88
|
let alarmSnoozeDuration = 10;
|
|
97
89
|
if (params.alarmSnoozeDuration !== undefined && params.alarmSnoozeDuration !== null) {
|
|
98
90
|
if (typeof params.alarmSnoozeDuration !== "number") {
|
|
99
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Invalid alarmSnoozeDuration type`);
|
|
100
91
|
throw new Error("alarmSnoozeDuration must be a number");
|
|
101
92
|
}
|
|
102
93
|
if (!ALARM_SNOOZE_DURATION_VALUES.includes(params.alarmSnoozeDuration)) {
|
|
103
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Invalid alarmSnoozeDuration value: ${params.alarmSnoozeDuration}`);
|
|
104
94
|
throw new Error(`alarmSnoozeDuration must be one of: ${ALARM_SNOOZE_DURATION_VALUES.join(", ")}`);
|
|
105
95
|
}
|
|
106
96
|
alarmSnoozeDuration = params.alarmSnoozeDuration;
|
|
107
97
|
}
|
|
108
|
-
logger.log(`[CREATE_ALARM_TOOL] - alarmSnoozeDuration: ${alarmSnoozeDuration}`);
|
|
109
98
|
// alarmSnoozeTotal - default 0
|
|
110
99
|
let alarmSnoozeTotal = 0;
|
|
111
100
|
if (params.alarmSnoozeTotal !== undefined && params.alarmSnoozeTotal !== null) {
|
|
112
101
|
if (typeof params.alarmSnoozeTotal !== "number") {
|
|
113
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Invalid alarmSnoozeTotal type`);
|
|
114
102
|
throw new Error("alarmSnoozeTotal must be a number");
|
|
115
103
|
}
|
|
116
104
|
if (!ALARM_SNOOZE_TOTAL_VALUES.includes(params.alarmSnoozeTotal)) {
|
|
117
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Invalid alarmSnoozeTotal value: ${params.alarmSnoozeTotal}`);
|
|
118
105
|
throw new Error(`alarmSnoozeTotal must be one of: ${ALARM_SNOOZE_TOTAL_VALUES.join(", ")}`);
|
|
119
106
|
}
|
|
120
107
|
alarmSnoozeTotal = params.alarmSnoozeTotal;
|
|
121
108
|
}
|
|
122
|
-
logger.log(`[CREATE_ALARM_TOOL] - alarmSnoozeTotal: ${alarmSnoozeTotal}`);
|
|
123
109
|
// alarmRingDuration - default 20
|
|
124
110
|
let alarmRingDuration = 20;
|
|
125
111
|
if (params.alarmRingDuration !== undefined && params.alarmRingDuration !== null) {
|
|
126
112
|
if (typeof params.alarmRingDuration !== "number") {
|
|
127
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Invalid alarmRingDuration type`);
|
|
128
113
|
throw new Error("alarmRingDuration must be a number");
|
|
129
114
|
}
|
|
130
115
|
if (!ALARM_RING_DURATION_VALUES.includes(params.alarmRingDuration)) {
|
|
131
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Invalid alarmRingDuration value: ${params.alarmRingDuration}`);
|
|
132
116
|
throw new Error(`alarmRingDuration must be one of: ${ALARM_RING_DURATION_VALUES.join(", ")}`);
|
|
133
117
|
}
|
|
134
118
|
alarmRingDuration = params.alarmRingDuration;
|
|
135
119
|
}
|
|
136
|
-
logger.log(`[CREATE_ALARM_TOOL] - alarmRingDuration: ${alarmRingDuration}`);
|
|
137
120
|
// daysOfWakeType - default 0
|
|
138
121
|
let daysOfWakeType = 0;
|
|
139
122
|
if (params.daysOfWakeType !== undefined && params.daysOfWakeType !== null) {
|
|
140
123
|
if (typeof params.daysOfWakeType !== "number") {
|
|
141
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Invalid daysOfWakeType type`);
|
|
142
124
|
throw new Error("daysOfWakeType must be a number");
|
|
143
125
|
}
|
|
144
126
|
if (!DAYS_OF_WAKE_TYPE_VALUES.includes(params.daysOfWakeType)) {
|
|
145
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Invalid daysOfWakeType value: ${params.daysOfWakeType}`);
|
|
146
127
|
throw new Error(`daysOfWakeType must be one of: ${DAYS_OF_WAKE_TYPE_VALUES.join(", ")}`);
|
|
147
128
|
}
|
|
148
129
|
daysOfWakeType = params.daysOfWakeType;
|
|
149
130
|
}
|
|
150
|
-
logger.log(`[CREATE_ALARM_TOOL] - daysOfWakeType: ${daysOfWakeType}`);
|
|
151
131
|
// daysOfWeek - only required when daysOfWakeType is 3
|
|
152
132
|
let daysOfWeek = [];
|
|
153
133
|
if (daysOfWakeType === 3) {
|
|
154
134
|
if (!params.daysOfWeek) {
|
|
155
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Missing daysOfWeek when daysOfWakeType=3`);
|
|
156
135
|
throw new Error("daysOfWeek is required when daysOfWakeType is 3 (custom)");
|
|
157
136
|
}
|
|
158
137
|
// ===== 参数规范化:兼容数组和 JSON 字符串 =====
|
|
159
138
|
let normalizedDaysOfWeek = null;
|
|
160
139
|
// 情况1: 已经是数组
|
|
161
140
|
if (Array.isArray(params.daysOfWeek)) {
|
|
162
|
-
logger.log(`[CREATE_ALARM_TOOL] ✅ daysOfWeek is already an array`);
|
|
163
141
|
normalizedDaysOfWeek = params.daysOfWeek;
|
|
164
142
|
}
|
|
165
143
|
// 情况2: 是字符串,尝试解析为 JSON 数组
|
|
166
144
|
else if (typeof params.daysOfWeek === 'string') {
|
|
167
|
-
logger.log(`[CREATE_ALARM_TOOL] 🔄 daysOfWeek is a string, attempting to parse as JSON...`);
|
|
168
145
|
try {
|
|
169
146
|
const parsed = JSON.parse(params.daysOfWeek);
|
|
170
147
|
if (Array.isArray(parsed)) {
|
|
171
|
-
logger.log(`[CREATE_ALARM_TOOL] ✅ Successfully parsed JSON string to array`);
|
|
172
148
|
normalizedDaysOfWeek = parsed;
|
|
173
149
|
}
|
|
174
150
|
else {
|
|
175
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Parsed JSON is not an array:`, typeof parsed);
|
|
176
151
|
throw new Error("daysOfWeek must be an array or a JSON string representing an array");
|
|
177
152
|
}
|
|
178
153
|
}
|
|
179
154
|
catch (parseError) {
|
|
180
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Failed to parse daysOfWeek as JSON:`, parseError);
|
|
181
155
|
throw new Error(`daysOfWeek must be a valid JSON array string. Parse error: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
|
|
182
156
|
}
|
|
183
157
|
}
|
|
184
158
|
// 情况3: 其他类型,报错
|
|
185
159
|
else {
|
|
186
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Invalid daysOfWeek type:`, typeof params.daysOfWeek);
|
|
187
160
|
throw new Error(`daysOfWeek must be an array or a JSON string, got ${typeof params.daysOfWeek}`);
|
|
188
161
|
}
|
|
189
162
|
// 验证数组非空
|
|
190
163
|
if (!normalizedDaysOfWeek || normalizedDaysOfWeek.length === 0) {
|
|
191
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ daysOfWeek array is empty`);
|
|
192
164
|
throw new Error("daysOfWeek array cannot be empty");
|
|
193
165
|
}
|
|
194
166
|
// 验证数组长度必须为1
|
|
195
167
|
if (normalizedDaysOfWeek.length !== 1) {
|
|
196
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ daysOfWeek array length must be 1, got ${normalizedDaysOfWeek.length}`);
|
|
197
168
|
throw new Error("daysOfWeek 仅支持长度为1的数组。如果需要一周中不同的几天,需要多次调用此工具");
|
|
198
169
|
}
|
|
199
170
|
// Validate each day
|
|
200
171
|
for (const day of normalizedDaysOfWeek) {
|
|
201
172
|
if (typeof day !== "string" || !DAYS_OF_WEEK_VALUES.includes(day)) {
|
|
202
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Invalid day value: ${day}`);
|
|
203
173
|
throw new Error(`daysOfWeek must contain only: ${DAYS_OF_WEEK_VALUES.join(", ")}`);
|
|
204
174
|
}
|
|
205
175
|
}
|
|
206
176
|
daysOfWeek = normalizedDaysOfWeek;
|
|
207
|
-
logger.log(`[CREATE_ALARM_TOOL] - daysOfWeek: ${daysOfWeek.join(", ")}`);
|
|
208
177
|
}
|
|
209
178
|
else {
|
|
210
179
|
// daysOfWakeType is not 3, daysOfWeek should not be provided
|
|
211
180
|
if (params.daysOfWeek) {
|
|
212
|
-
logger.warn(`[CREATE_ALARM_TOOL] ⚠️ daysOfWeek parameter is ignored when daysOfWakeType is not 3 (current: ${daysOfWakeType}). Please remove daysOfWeek parameter.`);
|
|
213
181
|
}
|
|
214
182
|
// Explicitly set to empty array
|
|
215
183
|
daysOfWeek = [];
|
|
216
184
|
}
|
|
217
185
|
// Get session context
|
|
218
|
-
logger.log(`[CREATE_ALARM_TOOL] 🔍 Attempting to get session context...`);
|
|
219
186
|
const sessionContext = getCurrentSessionContext();
|
|
220
187
|
if (!sessionContext) {
|
|
221
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ FAILED: No active session found!`);
|
|
222
|
-
logger.error(`[CREATE_ALARM_TOOL] - toolCallId: ${toolCallId}`);
|
|
223
188
|
throw new Error("No active XY session found. Create alarm tool can only be used during an active conversation.");
|
|
224
189
|
}
|
|
225
|
-
logger.log(`[CREATE_ALARM_TOOL] ✅ Session context found`);
|
|
226
|
-
logger.log(`[CREATE_ALARM_TOOL] - sessionId: ${sessionContext.sessionId}`);
|
|
227
|
-
logger.log(`[CREATE_ALARM_TOOL] - taskId: ${sessionContext.taskId}`);
|
|
228
|
-
logger.log(`[CREATE_ALARM_TOOL] - messageId: ${sessionContext.messageId}`);
|
|
229
190
|
const { config, sessionId, taskId, messageId } = sessionContext;
|
|
230
191
|
// Get WebSocket manager
|
|
231
|
-
logger.log(`[CREATE_ALARM_TOOL] 🔌 Getting WebSocket manager...`);
|
|
232
192
|
const wsManager = getXYWebSocketManager(config);
|
|
233
|
-
logger.log(`[CREATE_ALARM_TOOL] ✅ WebSocket manager obtained`);
|
|
234
193
|
// Build CreateAlarm command
|
|
235
|
-
logger.log(`[CREATE_ALARM_TOOL] 📦 Building CreateAlarm command...`);
|
|
236
194
|
// Build intentParam - only include daysOfWeek when daysOfWakeType is 3
|
|
237
195
|
const intentParam = {
|
|
238
196
|
entityName: "Alarm",
|
|
@@ -246,10 +204,8 @@ b. 使用该工具之前需获取当前真实时间`,
|
|
|
246
204
|
// Only include daysOfWeek when daysOfWakeType is 3
|
|
247
205
|
if (daysOfWakeType === 3 && daysOfWeek.length > 0) {
|
|
248
206
|
intentParam.daysOfWeek = daysOfWeek;
|
|
249
|
-
logger.log(`[CREATE_ALARM_TOOL] - Including daysOfWeek in intentParam: ${daysOfWeek.join(", ")}`);
|
|
250
207
|
}
|
|
251
208
|
else {
|
|
252
|
-
logger.log(`[CREATE_ALARM_TOOL] - Excluding daysOfWeek from intentParam (daysOfWakeType=${daysOfWakeType})`);
|
|
253
209
|
}
|
|
254
210
|
const command = {
|
|
255
211
|
header: {
|
|
@@ -283,25 +239,17 @@ b. 使用该工具之前需获取当前真实时间`,
|
|
|
283
239
|
},
|
|
284
240
|
};
|
|
285
241
|
// Send command and wait for response (60 second timeout)
|
|
286
|
-
logger.log(`[CREATE_ALARM_TOOL] ⏳ Setting up promise to wait for alarm creation response...`);
|
|
287
|
-
logger.log(`[CREATE_ALARM_TOOL] - Timeout: 60 seconds`);
|
|
288
242
|
return new Promise((resolve, reject) => {
|
|
289
243
|
const timeout = setTimeout(() => {
|
|
290
|
-
logger.error(`[CREATE_ALARM_TOOL] ⏰ Timeout: No response received within 60 seconds`);
|
|
291
244
|
wsManager.off("data-event", handler);
|
|
292
245
|
reject(new Error("创建闹钟超时(60秒)"));
|
|
293
246
|
}, 60000);
|
|
294
247
|
// Listen for data events from WebSocket
|
|
295
248
|
const handler = (event) => {
|
|
296
|
-
logger.log(`[CREATE_ALARM_TOOL] 📨 Received data event:`, JSON.stringify(event));
|
|
297
249
|
if (event.intentName === "CreateAlarm") {
|
|
298
|
-
logger.log(`[CREATE_ALARM_TOOL] 🎯 CreateAlarm event received`);
|
|
299
|
-
logger.log(`[CREATE_ALARM_TOOL] - status: ${event.status}`);
|
|
300
250
|
clearTimeout(timeout);
|
|
301
251
|
wsManager.off("data-event", handler);
|
|
302
252
|
if (event.status === "success" && event.outputs) {
|
|
303
|
-
logger.log(`[CREATE_ALARM_TOOL] ✅ Alarm creation completed successfully`);
|
|
304
|
-
logger.log(`[CREATE_ALARM_TOOL] - outputs:`, JSON.stringify(event.outputs));
|
|
305
253
|
// 成功,直接返回完整的 event.outputs JSON 字符串
|
|
306
254
|
resolve({
|
|
307
255
|
content: [
|
|
@@ -313,18 +261,13 @@ b. 使用该工具之前需获取当前真实时间`,
|
|
|
313
261
|
});
|
|
314
262
|
}
|
|
315
263
|
else {
|
|
316
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Alarm creation failed`);
|
|
317
|
-
logger.error(`[CREATE_ALARM_TOOL] - status: ${event.status}`);
|
|
318
|
-
logger.error(`[CREATE_ALARM_TOOL] - outputs:`, JSON.stringify(event.outputs || {}));
|
|
319
264
|
reject(new Error(`创建闹钟失败: ${event.status}`));
|
|
320
265
|
}
|
|
321
266
|
}
|
|
322
267
|
};
|
|
323
268
|
// Register event handler
|
|
324
|
-
logger.log(`[CREATE_ALARM_TOOL] 📡 Registering data-event handler on WebSocket manager`);
|
|
325
269
|
wsManager.on("data-event", handler);
|
|
326
270
|
// Send the command
|
|
327
|
-
logger.log(`[CREATE_ALARM_TOOL] 📤 Sending CreateAlarm command...`);
|
|
328
271
|
sendCommand({
|
|
329
272
|
config,
|
|
330
273
|
sessionId,
|
|
@@ -333,10 +276,8 @@ b. 使用该工具之前需获取当前真实时间`,
|
|
|
333
276
|
command,
|
|
334
277
|
})
|
|
335
278
|
.then(() => {
|
|
336
|
-
logger.log(`[CREATE_ALARM_TOOL] ✅ Command sent successfully, waiting for response...`);
|
|
337
279
|
})
|
|
338
280
|
.catch((error) => {
|
|
339
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Failed to send command:`, error);
|
|
340
281
|
clearTimeout(timeout);
|
|
341
282
|
wsManager.off("data-event", handler);
|
|
342
283
|
reject(error);
|
|
@@ -356,17 +297,14 @@ function parseAlarmTimeToTimestamp(alarmTime) {
|
|
|
356
297
|
const trimmed = alarmTime.trim();
|
|
357
298
|
// Check basic format (should have at least 13 characters: YYYYMMDD hhmmss)
|
|
358
299
|
if (trimmed.length < 13) {
|
|
359
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ alarmTime too short: ${trimmed}`);
|
|
360
300
|
return null;
|
|
361
301
|
}
|
|
362
302
|
// Extract date and time parts
|
|
363
303
|
// Format: YYYYMMDD hhmmss
|
|
364
304
|
const datePart = trimmed.substring(0, 8); // YYYYMMDD
|
|
365
305
|
const timePart = trimmed.substring(8).trim(); // hhmmss (may have leading space)
|
|
366
|
-
logger.log(`[CREATE_ALARM_TOOL] - datePart: ${datePart}, timePart: ${timePart}`);
|
|
367
306
|
// Validate lengths
|
|
368
307
|
if (datePart.length !== 8 || timePart.length !== 6) {
|
|
369
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Invalid part lengths: datePart=${datePart.length}, timePart=${timePart.length}`);
|
|
370
308
|
return null;
|
|
371
309
|
}
|
|
372
310
|
// Parse components
|
|
@@ -376,45 +314,36 @@ function parseAlarmTimeToTimestamp(alarmTime) {
|
|
|
376
314
|
const hour = parseInt(timePart.substring(0, 2), 10);
|
|
377
315
|
const minute = parseInt(timePart.substring(2, 4), 10);
|
|
378
316
|
const second = parseInt(timePart.substring(4, 6), 10);
|
|
379
|
-
logger.log(`[CREATE_ALARM_TOOL] - Parsed: ${year}-${month}-${day} ${hour}:${minute}:${second}`);
|
|
380
317
|
// Validate values
|
|
381
318
|
if (isNaN(year) || isNaN(month) || isNaN(day) ||
|
|
382
319
|
isNaN(hour) || isNaN(minute) || isNaN(second)) {
|
|
383
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ NaN detected in parsed values`);
|
|
384
320
|
return null;
|
|
385
321
|
}
|
|
386
322
|
// Validate ranges
|
|
387
323
|
if (month < 1 || month > 12) {
|
|
388
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Invalid month: ${month}`);
|
|
389
324
|
return null;
|
|
390
325
|
}
|
|
391
326
|
if (day < 1 || day > 31) {
|
|
392
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Invalid day: ${day}`);
|
|
393
327
|
return null;
|
|
394
328
|
}
|
|
395
329
|
if (hour < 0 || hour > 23) {
|
|
396
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Invalid hour: ${hour}`);
|
|
397
330
|
return null;
|
|
398
331
|
}
|
|
399
332
|
if (minute < 0 || minute > 59) {
|
|
400
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Invalid minute: ${minute}`);
|
|
401
333
|
return null;
|
|
402
334
|
}
|
|
403
335
|
if (second < 0 || second > 59) {
|
|
404
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Invalid second: ${second}`);
|
|
405
336
|
return null;
|
|
406
337
|
}
|
|
407
338
|
// Create Date object and get timestamp
|
|
408
339
|
const date = new Date(year, month - 1, day, hour, minute, second);
|
|
409
340
|
const timestamp = date.getTime();
|
|
410
341
|
if (isNaN(timestamp)) {
|
|
411
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Generated timestamp is NaN`);
|
|
412
342
|
return null;
|
|
413
343
|
}
|
|
414
344
|
return timestamp;
|
|
415
345
|
}
|
|
416
346
|
catch (error) {
|
|
417
|
-
logger.error(`[CREATE_ALARM_TOOL] ❌ Exception in parseAlarmTimeToTimestamp:`, error);
|
|
418
347
|
return null;
|
|
419
348
|
}
|
|
420
349
|
}
|