@ynhcj/xiaoyi-channel 0.0.25-beta → 0.0.27-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.
Files changed (33) hide show
  1. package/dist/src/bot.js +13 -3
  2. package/dist/src/channel.js +10 -1
  3. package/dist/src/tools/calendar-tool.js +2 -2
  4. package/dist/src/tools/call-phone-tool.d.ts +5 -0
  5. package/dist/src/tools/call-phone-tool.js +183 -0
  6. package/dist/src/tools/create-alarm-tool.d.ts +7 -0
  7. package/dist/src/tools/create-alarm-tool.js +395 -0
  8. package/dist/src/tools/delete-alarm-tool.d.ts +11 -0
  9. package/dist/src/tools/delete-alarm-tool.js +238 -0
  10. package/dist/src/tools/location-tool.js +2 -2
  11. package/dist/src/tools/modify-alarm-tool.d.ts +9 -0
  12. package/dist/src/tools/modify-alarm-tool.js +433 -0
  13. package/dist/src/tools/modify-note-tool.js +2 -2
  14. package/dist/src/tools/note-tool.js +2 -2
  15. package/dist/src/tools/search-alarm-tool.d.ts +8 -0
  16. package/dist/src/tools/search-alarm-tool.js +389 -0
  17. package/dist/src/tools/search-calendar-tool.js +2 -2
  18. package/dist/src/tools/search-contact-tool.js +2 -2
  19. package/dist/src/tools/search-file-tool.d.ts +5 -0
  20. package/dist/src/tools/search-file-tool.js +173 -0
  21. package/dist/src/tools/search-message-tool.d.ts +5 -0
  22. package/dist/src/tools/search-message-tool.js +173 -0
  23. package/dist/src/tools/search-note-tool.js +2 -2
  24. package/dist/src/tools/search-photo-gallery-tool.js +2 -2
  25. package/dist/src/tools/send-message-tool.d.ts +5 -0
  26. package/dist/src/tools/send-message-tool.js +189 -0
  27. package/dist/src/tools/session-manager.d.ts +12 -0
  28. package/dist/src/tools/session-manager.js +33 -0
  29. package/dist/src/tools/upload-file-tool.d.ts +13 -0
  30. package/dist/src/tools/upload-file-tool.js +265 -0
  31. package/dist/src/tools/upload-photo-tool.js +2 -2
  32. package/dist/src/tools/xiaoyi-gui-tool.js +8 -8
  33. package/package.json +1 -1
package/dist/src/bot.js CHANGED
@@ -4,7 +4,7 @@ import { parseA2AMessage, extractTextFromParts, extractFileParts, extractPushId
4
4
  import { downloadFilesFromParts } from "./file-download.js";
5
5
  import { resolveXYConfig } from "./config.js";
6
6
  import { sendStatusUpdate, sendClearContextResponse, sendTasksCancelResponse } from "./formatter.js";
7
- import { registerSession, unregisterSession } from "./tools/session-manager.js";
7
+ import { registerSession, unregisterSession, runWithSessionContext } from "./tools/session-manager.js";
8
8
  import { configManager } from "./utils/config-manager.js";
9
9
  import { registerTaskId, decrementTaskIdRef, lockTaskId, unlockTaskId, hasActiveTask, } from "./task-manager.js";
10
10
  /**
@@ -200,6 +200,14 @@ export async function handleXYMessage(params) {
200
200
  log(`xy: dispatching to agent (session=${parsed.sessionId})`);
201
201
  // Dispatch to OpenClaw core using correct API (following feishu pattern)
202
202
  log(`[BOT] 🚀 Starting dispatcher with session: ${route.sessionKey}`);
203
+ // Build session context for AsyncLocalStorage
204
+ const sessionContext = {
205
+ config,
206
+ sessionId: parsed.sessionId,
207
+ taskId: parsed.taskId,
208
+ messageId: parsed.messageId,
209
+ agentId: route.accountId,
210
+ };
203
211
  await core.channel.reply.withReplyDispatcher({
204
212
  dispatcher,
205
213
  onSettled: () => {
@@ -217,12 +225,14 @@ export async function handleXYMessage(params) {
217
225
  unregisterSession(route.sessionKey);
218
226
  log(`[BOT] ✅ Cleanup completed`);
219
227
  },
220
- run: () => core.channel.reply.dispatchReplyFromConfig({
228
+ run: () =>
229
+ // 🔐 Use AsyncLocalStorage to provide session context to tools
230
+ runWithSessionContext(sessionContext, () => core.channel.reply.dispatchReplyFromConfig({
221
231
  ctx: ctxPayload,
222
232
  cfg,
223
233
  dispatcher,
224
234
  replyOptions,
225
- }),
235
+ })),
226
236
  });
227
237
  log(`[BOT] ✅ Dispatcher completed for session: ${parsed.sessionId}`);
228
238
  log(`xy: dispatch complete (session=${parsed.sessionId})`);
@@ -12,6 +12,15 @@ import { searchCalendarTool } from "./tools/search-calendar-tool.js";
12
12
  import { searchPhotoGalleryTool } from "./tools/search-photo-gallery-tool.js";
13
13
  import { uploadPhotoTool } from "./tools/upload-photo-tool.js";
14
14
  import { xiaoyiGuiTool } from "./tools/xiaoyi-gui-tool.js";
15
+ import { callPhoneTool } from "./tools/call-phone-tool.js";
16
+ import { searchMessageTool } from "./tools/search-message-tool.js";
17
+ import { searchFileTool } from "./tools/search-file-tool.js";
18
+ import { uploadFileTool } from "./tools/upload-file-tool.js";
19
+ import { createAlarmTool } from "./tools/create-alarm-tool.js";
20
+ import { searchAlarmTool } from "./tools/search-alarm-tool.js";
21
+ import { modifyAlarmTool } from "./tools/modify-alarm-tool.js";
22
+ import { deleteAlarmTool } from "./tools/delete-alarm-tool.js";
23
+ import { sendMessageTool } from "./tools/send-message-tool.js";
15
24
  /**
16
25
  * Xiaoyi Channel Plugin for OpenClaw.
17
26
  * Implements Xiaoyi A2A protocol with dual WebSocket connections.
@@ -51,7 +60,7 @@ export const xyPlugin = {
51
60
  },
52
61
  outbound: xyOutbound,
53
62
  onboarding: xyOnboardingAdapter,
54
- agentTools: [locationTool, noteTool, searchNoteTool, modifyNoteTool, calendarTool, searchCalendarTool, searchPhotoGalleryTool, uploadPhotoTool, xiaoyiGuiTool], // searchContactTool 已暂时禁用
63
+ agentTools: [locationTool, noteTool, searchNoteTool, modifyNoteTool, calendarTool, searchCalendarTool, searchPhotoGalleryTool, uploadPhotoTool, xiaoyiGuiTool, callPhoneTool, searchMessageTool, searchFileTool, uploadFileTool, createAlarmTool, searchAlarmTool, modifyAlarmTool, deleteAlarmTool, sendMessageTool], // searchContactTool 已暂时禁用
55
64
  messaging: {
56
65
  normalizeTarget: (raw) => {
57
66
  const trimmed = raw.trim();
@@ -1,6 +1,6 @@
1
1
  import { getXYWebSocketManager } from "../client.js";
2
2
  import { sendCommand } from "../formatter.js";
3
- import { getLatestSessionContext } from "./session-manager.js";
3
+ import { getCurrentSessionContext } from "./session-manager.js";
4
4
  import { logger } from "../utils/logger.js";
5
5
  /**
6
6
  * XY calendar event tool - creates a calendar event on user's device.
@@ -54,7 +54,7 @@ export const calendarTool = {
54
54
  logger.log(`[CALENDAR_TOOL] - dtEnd timestamp: ${dtEndMs}`);
55
55
  // Get session context
56
56
  logger.log(`[CALENDAR_TOOL] 🔍 Attempting to get session context...`);
57
- const sessionContext = getLatestSessionContext();
57
+ const sessionContext = getCurrentSessionContext();
58
58
  if (!sessionContext) {
59
59
  logger.error(`[CALENDAR_TOOL] ❌ FAILED: No active session found!`);
60
60
  logger.error(`[CALENDAR_TOOL] - toolCallId: ${toolCallId}`);
@@ -0,0 +1,5 @@
1
+ /**
2
+ * XY call phone tool - makes a phone call on user's device.
3
+ * Requires phoneNumber parameter and optional slotId (0 for primary SIM, 1 for secondary SIM).
4
+ */
5
+ export declare const callPhoneTool: any;
@@ -0,0 +1,183 @@
1
+ import { getXYWebSocketManager } from "../client.js";
2
+ import { sendCommand } from "../formatter.js";
3
+ import { getCurrentSessionContext } from "./session-manager.js";
4
+ import { logger } from "../utils/logger.js";
5
+ /**
6
+ * XY call phone tool - makes a phone call on user's device.
7
+ * Requires phoneNumber parameter and optional slotId (0 for primary SIM, 1 for secondary SIM).
8
+ */
9
+ export const callPhoneTool = {
10
+ name: "call_phone",
11
+ label: "Call Phone",
12
+ description: "拨打电话。需要提供要拨打的电话号码。slotId参数可选,默认为0(主卡),如果用户明确要求使用副卡则设置为1。注意:操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。",
13
+ parameters: {
14
+ type: "object",
15
+ properties: {
16
+ phoneNumber: {
17
+ type: "string",
18
+ description: "要拨打的电话号码",
19
+ },
20
+ slotId: {
21
+ type: "number",
22
+ description: "SIM卡槽ID,默认为0(主卡),设置为1表示副卡。仅当用户明确要求使用副卡时才设置为1",
23
+ default: 0,
24
+ },
25
+ },
26
+ required: ["phoneNumber"],
27
+ },
28
+ 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
+ // Validate phoneNumber parameter
34
+ if (!params.phoneNumber || typeof params.phoneNumber !== "string" || params.phoneNumber.trim() === "") {
35
+ logger.error(`[CALL_PHONE_TOOL] ❌ Missing or invalid phoneNumber parameter`);
36
+ throw new Error("Missing required parameter: phoneNumber must be a non-empty string");
37
+ }
38
+ // Set default slotId if not provided
39
+ const slotId = params.slotId !== undefined && params.slotId !== null ? params.slotId : 0;
40
+ // Validate slotId (must be 0 or 1)
41
+ if (slotId !== 0 && slotId !== 1) {
42
+ logger.error(`[CALL_PHONE_TOOL] ❌ Invalid slotId: ${slotId}`);
43
+ throw new Error("Invalid slotId: must be 0 (primary SIM) or 1 (secondary SIM)");
44
+ }
45
+ logger.log(`[CALL_PHONE_TOOL] 📞 Preparing to call phone number: ${params.phoneNumber}`);
46
+ logger.log(`[CALL_PHONE_TOOL] - slotId: ${slotId} (${slotId === 0 ? "主卡" : "副卡"})`);
47
+ // Get session context
48
+ logger.log(`[CALL_PHONE_TOOL] 🔍 Attempting to get session context...`);
49
+ const sessionContext = getCurrentSessionContext();
50
+ if (!sessionContext) {
51
+ logger.error(`[CALL_PHONE_TOOL] ❌ FAILED: No active session found!`);
52
+ logger.error(`[CALL_PHONE_TOOL] - toolCallId: ${toolCallId}`);
53
+ throw new Error("No active XY session found. Call phone tool can only be used during an active conversation.");
54
+ }
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
+ const { config, sessionId, taskId, messageId } = sessionContext;
60
+ // Get WebSocket manager
61
+ logger.log(`[CALL_PHONE_TOOL] 🔌 Getting WebSocket manager...`);
62
+ const wsManager = getXYWebSocketManager(config);
63
+ logger.log(`[CALL_PHONE_TOOL] ✅ WebSocket manager obtained`);
64
+ // Build StartCall command
65
+ logger.log(`[CALL_PHONE_TOOL] 📦 Building StartCall command...`);
66
+ const command = {
67
+ header: {
68
+ namespace: "Common",
69
+ name: "Action",
70
+ },
71
+ payload: {
72
+ cardParam: {},
73
+ executeParam: {
74
+ executeMode: "background",
75
+ intentName: "StartCall",
76
+ bundleName: "com.huawei.hmos.aidispatchservice",
77
+ dimension: "",
78
+ needUnlock: true,
79
+ actionResponse: true,
80
+ timeOut: 5,
81
+ intentParam: {
82
+ phoneNumber: params.phoneNumber.trim(),
83
+ slotId: slotId,
84
+ },
85
+ permissionId: [],
86
+ achieveType: "INTENT",
87
+ },
88
+ responses: [
89
+ {
90
+ resultCode: "",
91
+ displayText: "",
92
+ ttsText: "",
93
+ },
94
+ ],
95
+ needUploadResult: true,
96
+ noHalfPage: false,
97
+ pageControlRelated: false,
98
+ },
99
+ };
100
+ logger.log(`[CALL_PHONE_TOOL] 📋 Command details:`, JSON.stringify(command, null, 2));
101
+ // 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
+ return new Promise((resolve, reject) => {
105
+ const timeout = setTimeout(() => {
106
+ logger.error(`[CALL_PHONE_TOOL] ⏰ Timeout: No response received within 60 seconds`);
107
+ wsManager.off("data-event", handler);
108
+ reject(new Error("拨打电话超时(60秒)"));
109
+ }, 60000);
110
+ // Listen for data events from WebSocket
111
+ const handler = (event) => {
112
+ logger.log(`[CALL_PHONE_TOOL] 📨 Received data event:`, JSON.stringify(event));
113
+ if (event.intentName === "StartCall") {
114
+ logger.log(`[CALL_PHONE_TOOL] 🎯 StartCall event received`);
115
+ logger.log(`[CALL_PHONE_TOOL] - status: ${event.status}`);
116
+ clearTimeout(timeout);
117
+ wsManager.off("data-event", handler);
118
+ 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
+ // Check for error code in outputs
122
+ const code = event.outputs.code !== undefined ? event.outputs.code : null;
123
+ if (code !== null && code !== 0) {
124
+ logger.error(`[CALL_PHONE_TOOL] ❌ Device returned error`);
125
+ logger.error(`[CALL_PHONE_TOOL] - code: ${code}`);
126
+ const errorMsg = event.outputs.errorMsg || event.outputs.errMsg || "未知错误";
127
+ logger.error(`[CALL_PHONE_TOOL] - errorMsg: ${errorMsg}`);
128
+ reject(new Error(`拨打电话失败: ${errorMsg} (错误代码: ${code})`));
129
+ return;
130
+ }
131
+ // Return the outputs directly
132
+ const result = {
133
+ success: true,
134
+ code: code,
135
+ phoneNumber: params.phoneNumber,
136
+ slotId: slotId,
137
+ message: "电话拨打成功",
138
+ };
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
+ resolve({
143
+ content: [
144
+ {
145
+ type: "text",
146
+ text: JSON.stringify(result),
147
+ },
148
+ ],
149
+ });
150
+ }
151
+ 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
+ const errorDetail = event.outputs ? JSON.stringify(event.outputs) : event.status;
156
+ reject(new Error(`拨打电话失败: ${errorDetail}`));
157
+ }
158
+ }
159
+ };
160
+ // Register event handler
161
+ logger.log(`[CALL_PHONE_TOOL] 📡 Registering data-event handler on WebSocket manager`);
162
+ wsManager.on("data-event", handler);
163
+ // Send the command
164
+ logger.log(`[CALL_PHONE_TOOL] 📤 Sending StartCall command...`);
165
+ sendCommand({
166
+ config,
167
+ sessionId,
168
+ taskId,
169
+ messageId,
170
+ command,
171
+ })
172
+ .then(() => {
173
+ logger.log(`[CALL_PHONE_TOOL] ✅ Command sent successfully, waiting for response...`);
174
+ })
175
+ .catch((error) => {
176
+ logger.error(`[CALL_PHONE_TOOL] ❌ Failed to send command:`, error);
177
+ clearTimeout(timeout);
178
+ wsManager.off("data-event", handler);
179
+ reject(error);
180
+ });
181
+ });
182
+ },
183
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * XY create alarm tool - creates an alarm on user's device.
3
+ * Requires alarmTime parameter. Other parameters are optional with sensible defaults.
4
+ *
5
+ * Time format: YYYYMMDD hhmmss (e.g., 20240315 143000)
6
+ */
7
+ export declare const createAlarmTool: any;