@ynhcj/xiaoyi-channel 0.0.26-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 +2 -2
  33. package/package.json +1 -1
@@ -0,0 +1,173 @@
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 search message tool - searches SMS messages on user's device.
7
+ * Returns matching messages based on content keyword search.
8
+ */
9
+ export const searchMessageTool = {
10
+ name: "search_message",
11
+ label: "Search Message",
12
+ description: "搜索手机短信。根据关键词搜索短信内容。注意:操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。",
13
+ parameters: {
14
+ type: "object",
15
+ properties: {
16
+ content: {
17
+ type: "string",
18
+ description: "搜索关键词,用于在短信内容中进行匹配",
19
+ },
20
+ },
21
+ required: ["content"],
22
+ },
23
+ 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
+ // Validate content parameter
29
+ if (!params.content || typeof params.content !== "string" || params.content.trim() === "") {
30
+ logger.error(`[SEARCH_MESSAGE_TOOL] ❌ Missing or invalid content parameter`);
31
+ throw new Error("Missing required parameter: content must be a non-empty string");
32
+ }
33
+ logger.log(`[SEARCH_MESSAGE_TOOL] 🔍 Searching for messages with keyword: ${params.content}`);
34
+ // Get session context
35
+ logger.log(`[SEARCH_MESSAGE_TOOL] 🔍 Attempting to get session context...`);
36
+ const sessionContext = getCurrentSessionContext();
37
+ if (!sessionContext) {
38
+ logger.error(`[SEARCH_MESSAGE_TOOL] ❌ FAILED: No active session found!`);
39
+ logger.error(`[SEARCH_MESSAGE_TOOL] - toolCallId: ${toolCallId}`);
40
+ throw new Error("No active XY session found. Search message tool can only be used during an active conversation.");
41
+ }
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
+ const { config, sessionId, taskId, messageId } = sessionContext;
47
+ // Get WebSocket manager
48
+ logger.log(`[SEARCH_MESSAGE_TOOL] 🔌 Getting WebSocket manager...`);
49
+ const wsManager = getXYWebSocketManager(config);
50
+ logger.log(`[SEARCH_MESSAGE_TOOL] ✅ WebSocket manager obtained`);
51
+ // Build SearchMessage command
52
+ logger.log(`[SEARCH_MESSAGE_TOOL] 📦 Building SearchMessage command...`);
53
+ const command = {
54
+ header: {
55
+ namespace: "Common",
56
+ name: "Action",
57
+ },
58
+ payload: {
59
+ cardParam: {},
60
+ executeParam: {
61
+ executeMode: "background",
62
+ intentName: "SearchMessage",
63
+ bundleName: "com.huawei.hmos.aidispatchservice",
64
+ needUnlock: true,
65
+ actionResponse: true,
66
+ appType: "OHOS_APP",
67
+ timeOut: 5,
68
+ intentParam: {
69
+ content: params.content.trim(),
70
+ },
71
+ permissionId: [],
72
+ achieveType: "INTENT",
73
+ },
74
+ responses: [
75
+ {
76
+ resultCode: "",
77
+ displayText: "",
78
+ ttsText: "",
79
+ },
80
+ ],
81
+ needUploadResult: true,
82
+ noHalfPage: false,
83
+ pageControlRelated: false,
84
+ },
85
+ };
86
+ logger.log(`[SEARCH_MESSAGE_TOOL] 📋 Command details:`, JSON.stringify(command, null, 2));
87
+ // 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
+ return new Promise((resolve, reject) => {
91
+ const timeout = setTimeout(() => {
92
+ logger.error(`[SEARCH_MESSAGE_TOOL] ⏰ Timeout: No response received within 60 seconds`);
93
+ wsManager.off("data-event", handler);
94
+ reject(new Error("搜索短信超时(60秒)"));
95
+ }, 60000);
96
+ // Listen for data events from WebSocket
97
+ const handler = (event) => {
98
+ logger.log(`[SEARCH_MESSAGE_TOOL] 📨 Received data event:`, JSON.stringify(event));
99
+ if (event.intentName === "SearchMessage") {
100
+ logger.log(`[SEARCH_MESSAGE_TOOL] 🎯 SearchMessage event received`);
101
+ logger.log(`[SEARCH_MESSAGE_TOOL] - status: ${event.status}`);
102
+ clearTimeout(timeout);
103
+ wsManager.off("data-event", handler);
104
+ if (event.status === "success" && event.outputs) {
105
+ logger.log(`[SEARCH_MESSAGE_TOOL] ✅ Message search response received`);
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}`);
132
+ resolve({
133
+ content: [
134
+ {
135
+ type: "text",
136
+ text: JSON.stringify(items),
137
+ },
138
+ ],
139
+ });
140
+ }
141
+ 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
+ const errorDetail = event.outputs ? JSON.stringify(event.outputs) : event.status;
146
+ reject(new Error(`搜索短信失败: ${errorDetail}`));
147
+ }
148
+ }
149
+ };
150
+ // Register event handler
151
+ logger.log(`[SEARCH_MESSAGE_TOOL] 📡 Registering data-event handler on WebSocket manager`);
152
+ wsManager.on("data-event", handler);
153
+ // Send the command
154
+ logger.log(`[SEARCH_MESSAGE_TOOL] 📤 Sending SearchMessage command...`);
155
+ sendCommand({
156
+ config,
157
+ sessionId,
158
+ taskId,
159
+ messageId,
160
+ command,
161
+ })
162
+ .then(() => {
163
+ logger.log(`[SEARCH_MESSAGE_TOOL] ✅ Command sent successfully, waiting for response...`);
164
+ })
165
+ .catch((error) => {
166
+ logger.error(`[SEARCH_MESSAGE_TOOL] ❌ Failed to send command:`, error);
167
+ clearTimeout(timeout);
168
+ wsManager.off("data-event", handler);
169
+ reject(error);
170
+ });
171
+ });
172
+ },
173
+ };
@@ -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 search note tool - searches notes on user's device.
@@ -27,7 +27,7 @@ export const searchNoteTool = {
27
27
  throw new Error("Missing required parameter: query is required");
28
28
  }
29
29
  // Get session context
30
- const sessionContext = getLatestSessionContext();
30
+ const sessionContext = getCurrentSessionContext();
31
31
  if (!sessionContext) {
32
32
  throw new Error("No active XY session found. Search note tool can only be used during an active conversation.");
33
33
  }
@@ -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 search photo gallery tool - searches photos in user's gallery.
@@ -46,7 +46,7 @@ export const searchPhotoGalleryTool = {
46
46
  }
47
47
  // Get session context
48
48
  logger.log(`[SEARCH_PHOTO_GALLERY_TOOL] 🔍 Attempting to get session context...`);
49
- const sessionContext = getLatestSessionContext();
49
+ const sessionContext = getCurrentSessionContext();
50
50
  if (!sessionContext) {
51
51
  logger.error(`[SEARCH_PHOTO_GALLERY_TOOL] ❌ FAILED: No active session found!`);
52
52
  logger.error(`[SEARCH_PHOTO_GALLERY_TOOL] - toolCallId: ${toolCallId}`);
@@ -0,0 +1,5 @@
1
+ /**
2
+ * XY send message tool - sends SMS message on user's device.
3
+ * Requires phoneNumber (with +86 prefix) and content parameters.
4
+ */
5
+ export declare const sendMessageTool: any;
@@ -0,0 +1,189 @@
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 send message tool - sends SMS message on user's device.
7
+ * Requires phoneNumber (with +86 prefix) and content parameters.
8
+ */
9
+ export const sendMessageTool = {
10
+ name: "send_message",
11
+ label: "Send Message",
12
+ description: "通过手机发送短信。需要提供接收方手机号码和短信内容。手机号码会自动添加+86前缀(如果没有的话)。注意:操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。",
13
+ parameters: {
14
+ type: "object",
15
+ properties: {
16
+ phoneNumber: {
17
+ type: "string",
18
+ description: "接收方手机号码(会自动添加+86前缀)",
19
+ },
20
+ content: {
21
+ type: "string",
22
+ description: "短信内容",
23
+ },
24
+ },
25
+ required: ["phoneNumber", "content"],
26
+ },
27
+ async execute(toolCallId, params) {
28
+ logger.log(`[SEND_MESSAGE_TOOL] 🚀 Starting execution`);
29
+ logger.log(`[SEND_MESSAGE_TOOL] - toolCallId: ${toolCallId}`);
30
+ logger.log(`[SEND_MESSAGE_TOOL] - params:`, JSON.stringify(params));
31
+ logger.log(`[SEND_MESSAGE_TOOL] - timestamp: ${new Date().toISOString()}`);
32
+ // Validate phoneNumber parameter
33
+ if (!params.phoneNumber || typeof params.phoneNumber !== "string" || params.phoneNumber.trim() === "") {
34
+ logger.error(`[SEND_MESSAGE_TOOL] ❌ Missing or invalid phoneNumber parameter`);
35
+ throw new Error("Missing required parameter: phoneNumber must be a non-empty string");
36
+ }
37
+ // Validate content parameter
38
+ if (!params.content || typeof params.content !== "string" || params.content.trim() === "") {
39
+ logger.error(`[SEND_MESSAGE_TOOL] ❌ Missing or invalid content parameter`);
40
+ throw new Error("Missing required parameter: content must be a non-empty string");
41
+ }
42
+ // Normalize phone number: add +86 prefix if not present
43
+ let phoneNumber = params.phoneNumber.trim();
44
+ if (!phoneNumber.startsWith("+86")) {
45
+ // Remove leading 0 if present (e.g., 086 -> 86)
46
+ if (phoneNumber.startsWith("0")) {
47
+ phoneNumber = phoneNumber.substring(1);
48
+ }
49
+ // Remove +86 or 86 prefix if already present to avoid duplication
50
+ if (phoneNumber.startsWith("86")) {
51
+ phoneNumber = phoneNumber.substring(2);
52
+ }
53
+ phoneNumber = `+86${phoneNumber}`;
54
+ logger.log(`[SEND_MESSAGE_TOOL] 📞 Normalized phone number: ${params.phoneNumber} -> ${phoneNumber}`);
55
+ }
56
+ logger.log(`[SEND_MESSAGE_TOOL] 📤 Preparing to send message`);
57
+ logger.log(`[SEND_MESSAGE_TOOL] - phoneNumber: ${phoneNumber}`);
58
+ logger.log(`[SEND_MESSAGE_TOOL] - content length: ${params.content.length} characters`);
59
+ // Get session context
60
+ logger.log(`[SEND_MESSAGE_TOOL] 🔍 Attempting to get session context...`);
61
+ const sessionContext = getCurrentSessionContext();
62
+ if (!sessionContext) {
63
+ logger.error(`[SEND_MESSAGE_TOOL] ❌ FAILED: No active session found!`);
64
+ logger.error(`[SEND_MESSAGE_TOOL] - toolCallId: ${toolCallId}`);
65
+ throw new Error("No active XY session found. Send message tool can only be used during an active conversation.");
66
+ }
67
+ logger.log(`[SEND_MESSAGE_TOOL] ✅ Session context found`);
68
+ logger.log(`[SEND_MESSAGE_TOOL] - sessionId: ${sessionContext.sessionId}`);
69
+ logger.log(`[SEND_MESSAGE_TOOL] - taskId: ${sessionContext.taskId}`);
70
+ logger.log(`[SEND_MESSAGE_TOOL] - messageId: ${sessionContext.messageId}`);
71
+ const { config, sessionId, taskId, messageId } = sessionContext;
72
+ // Get WebSocket manager
73
+ logger.log(`[SEND_MESSAGE_TOOL] 🔌 Getting WebSocket manager...`);
74
+ const wsManager = getXYWebSocketManager(config);
75
+ logger.log(`[SEND_MESSAGE_TOOL] ✅ WebSocket manager obtained`);
76
+ // Build SendShortMessage command
77
+ logger.log(`[SEND_MESSAGE_TOOL] 📦 Building SendShortMessage command...`);
78
+ const command = {
79
+ header: {
80
+ namespace: "Common",
81
+ name: "Action",
82
+ },
83
+ payload: {
84
+ cardParam: {},
85
+ executeParam: {
86
+ executeMode: "background",
87
+ intentName: "SendShortMessage",
88
+ bundleName: "com.huawei.hmos.aidispatchservice",
89
+ needUnlock: true,
90
+ actionResponse: true,
91
+ appType: "OHOS_APP",
92
+ timeOut: 5,
93
+ intentParam: {
94
+ phoneNumber: phoneNumber,
95
+ content: params.content.trim(),
96
+ },
97
+ permissionId: [],
98
+ achieveType: "INTENT",
99
+ },
100
+ responses: [
101
+ {
102
+ resultCode: "",
103
+ displayText: "",
104
+ ttsText: "",
105
+ },
106
+ ],
107
+ needUploadResult: true,
108
+ noHalfPage: false,
109
+ pageControlRelated: false,
110
+ },
111
+ };
112
+ logger.log(`[SEND_MESSAGE_TOOL] 📋 Command details:`, JSON.stringify(command, null, 2));
113
+ // Send command and wait for response (60 second timeout)
114
+ logger.log(`[SEND_MESSAGE_TOOL] ⏳ Setting up promise to wait for send message response...`);
115
+ logger.log(`[SEND_MESSAGE_TOOL] - Timeout: 60 seconds`);
116
+ return new Promise((resolve, reject) => {
117
+ const timeout = setTimeout(() => {
118
+ logger.error(`[SEND_MESSAGE_TOOL] ⏰ Timeout: No response received within 60 seconds`);
119
+ wsManager.off("data-event", handler);
120
+ reject(new Error("发送短信超时(60秒)"));
121
+ }, 60000);
122
+ // Listen for data events from WebSocket
123
+ const handler = (event) => {
124
+ logger.log(`[SEND_MESSAGE_TOOL] 📨 Received data event:`, JSON.stringify(event));
125
+ if (event.intentName === "SendShortMessage") {
126
+ logger.log(`[SEND_MESSAGE_TOOL] 🎯 SendShortMessage event received`);
127
+ logger.log(`[SEND_MESSAGE_TOOL] - status: ${event.status}`);
128
+ clearTimeout(timeout);
129
+ wsManager.off("data-event", handler);
130
+ if (event.status === "success" && event.outputs) {
131
+ logger.log(`[SEND_MESSAGE_TOOL] ✅ Send message response received`);
132
+ logger.log(`[SEND_MESSAGE_TOOL] - outputs:`, JSON.stringify(event.outputs));
133
+ // Check for error code in outputs
134
+ const code = event.outputs.code !== undefined ? event.outputs.code : null;
135
+ if (code !== null && code !== 0) {
136
+ logger.error(`[SEND_MESSAGE_TOOL] ❌ Device returned error`);
137
+ logger.error(`[SEND_MESSAGE_TOOL] - code: ${code}`);
138
+ const errorMsg = event.outputs.errorMsg || event.outputs.errMsg || "未知错误";
139
+ logger.error(`[SEND_MESSAGE_TOOL] - errorMsg: ${errorMsg}`);
140
+ reject(new Error(`发送短信失败: ${errorMsg} (错误代码: ${code})`));
141
+ return;
142
+ }
143
+ // Extract result with safe checks
144
+ const result = event.outputs.result || {};
145
+ logger.log(`[SEND_MESSAGE_TOOL] 🎉 Message sent successfully`);
146
+ logger.log(`[SEND_MESSAGE_TOOL] - phoneNumber: ${phoneNumber}`);
147
+ logger.log(`[SEND_MESSAGE_TOOL] - result:`, JSON.stringify(result));
148
+ resolve({
149
+ content: [
150
+ {
151
+ type: "text",
152
+ text: JSON.stringify(result),
153
+ },
154
+ ],
155
+ });
156
+ }
157
+ else {
158
+ logger.error(`[SEND_MESSAGE_TOOL] ❌ Send message failed`);
159
+ logger.error(`[SEND_MESSAGE_TOOL] - status: ${event.status}`);
160
+ logger.error(`[SEND_MESSAGE_TOOL] - outputs:`, JSON.stringify(event.outputs || {}));
161
+ const errorDetail = event.outputs ? JSON.stringify(event.outputs) : event.status;
162
+ reject(new Error(`发送短信失败: ${errorDetail}`));
163
+ }
164
+ }
165
+ };
166
+ // Register event handler
167
+ logger.log(`[SEND_MESSAGE_TOOL] 📡 Registering data-event handler on WebSocket manager`);
168
+ wsManager.on("data-event", handler);
169
+ // Send the command
170
+ logger.log(`[SEND_MESSAGE_TOOL] 📤 Sending SendShortMessage command...`);
171
+ sendCommand({
172
+ config,
173
+ sessionId,
174
+ taskId,
175
+ messageId,
176
+ command,
177
+ })
178
+ .then(() => {
179
+ logger.log(`[SEND_MESSAGE_TOOL] ✅ Command sent successfully, waiting for response...`);
180
+ })
181
+ .catch((error) => {
182
+ logger.error(`[SEND_MESSAGE_TOOL] ❌ Failed to send command:`, error);
183
+ clearTimeout(timeout);
184
+ wsManager.off("data-event", handler);
185
+ reject(error);
186
+ });
187
+ });
188
+ },
189
+ };
@@ -23,7 +23,19 @@ export declare function unregisterSession(sessionKey: string): void;
23
23
  export declare function getSessionContext(sessionKey: string): SessionContext | null;
24
24
  /**
25
25
  * Get the most recent session context.
26
+ * @deprecated Use getCurrentSessionContext() instead for thread-safe access.
26
27
  * This is a fallback for tools that don't have access to sessionKey.
27
28
  * Returns null if no sessions are active.
28
29
  */
29
30
  export declare function getLatestSessionContext(): SessionContext | null;
31
+ /**
32
+ * Run a callback with a session context stored in AsyncLocalStorage.
33
+ * This ensures thread-safe context isolation for concurrent requests.
34
+ */
35
+ export declare function runWithSessionContext<T>(context: SessionContext, callback: () => Promise<T>): Promise<T>;
36
+ /**
37
+ * Get the current session context from AsyncLocalStorage.
38
+ * This is the recommended way to access session context in tools.
39
+ * Returns null if not running within a session context.
40
+ */
41
+ export declare function getCurrentSessionContext(): SessionContext | null;
@@ -1,7 +1,12 @@
1
+ // Session manager for XY tool context
2
+ // Stores active session contexts that tools can access
3
+ import { AsyncLocalStorage } from "async_hooks";
1
4
  import { logger } from "../utils/logger.js";
2
5
  import { configManager } from "../utils/config-manager.js";
3
6
  // Map of sessionKey -> SessionContextWithRef
4
7
  const activeSessions = new Map();
8
+ // AsyncLocalStorage for thread-safe session context isolation
9
+ const asyncLocalStorage = new AsyncLocalStorage();
5
10
  /**
6
11
  * Register a session context for tool access.
7
12
  * Should be called when starting to process a message.
@@ -74,6 +79,7 @@ export function getSessionContext(sessionKey) {
74
79
  }
75
80
  /**
76
81
  * Get the most recent session context.
82
+ * @deprecated Use getCurrentSessionContext() instead for thread-safe access.
77
83
  * This is a fallback for tools that don't have access to sessionKey.
78
84
  * Returns null if no sessions are active.
79
85
  */
@@ -96,3 +102,30 @@ export function getLatestSessionContext() {
96
102
  const { refCount, ...latestSession } = latestSessionWithRef;
97
103
  return latestSession;
98
104
  }
105
+ /**
106
+ * Run a callback with a session context stored in AsyncLocalStorage.
107
+ * This ensures thread-safe context isolation for concurrent requests.
108
+ */
109
+ export function runWithSessionContext(context, callback) {
110
+ logger.log(`[SESSION_MANAGER] 🔐 Running with AsyncLocalStorage context`);
111
+ logger.log(`[SESSION_MANAGER] - sessionId: ${context.sessionId}`);
112
+ logger.log(`[SESSION_MANAGER] - taskId: ${context.taskId}`);
113
+ return asyncLocalStorage.run(context, callback);
114
+ }
115
+ /**
116
+ * Get the current session context from AsyncLocalStorage.
117
+ * This is the recommended way to access session context in tools.
118
+ * Returns null if not running within a session context.
119
+ */
120
+ export function getCurrentSessionContext() {
121
+ const context = asyncLocalStorage.getStore() ?? null;
122
+ if (context) {
123
+ logger.log(`[SESSION_MANAGER] ✅ Got current session context from AsyncLocalStorage`);
124
+ logger.log(`[SESSION_MANAGER] - sessionId: ${context.sessionId}`);
125
+ logger.log(`[SESSION_MANAGER] - taskId: ${context.taskId}`);
126
+ }
127
+ else {
128
+ logger.warn(`[SESSION_MANAGER] ⚠️ No session context in AsyncLocalStorage`);
129
+ }
130
+ return context;
131
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * XY upload file tool - uploads local files to get publicly accessible URLs.
3
+ * Requires file URIs from search_file tool as prerequisite.
4
+ *
5
+ * Prerequisites:
6
+ * 1. Call search_file tool first to get URIs of files
7
+ * 2. Use the URIs to get public URLs
8
+ *
9
+ * Usage Note:
10
+ * - After getting public URLs, if further processing is needed, download the file first
11
+ * - URLs returned are publicly accessible
12
+ */
13
+ export declare const uploadFileTool: any;