@ynhcj/xiaoyi-channel 0.0.167-next → 0.0.168-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.
Files changed (47) hide show
  1. package/dist/src/reply-dispatcher.js +36 -29
  2. package/dist/src/task-manager.d.ts +11 -0
  3. package/dist/src/task-manager.js +17 -0
  4. package/dist/src/tools/agent-as-skill-tool.js +3 -1
  5. package/dist/src/tools/calendar-tool.js +3 -1
  6. package/dist/src/tools/call-device-tool.js +3 -1
  7. package/dist/src/tools/call-phone-tool.js +3 -1
  8. package/dist/src/tools/check-plugin-privilege-tool.js +3 -1
  9. package/dist/src/tools/create-alarm-tool.js +3 -1
  10. package/dist/src/tools/delete-alarm-tool.js +3 -1
  11. package/dist/src/tools/discover-cross-devices-tool.js +3 -1
  12. package/dist/src/tools/display-a2ui-card-tool.js +3 -1
  13. package/dist/src/tools/image-reading-tool.js +3 -1
  14. package/dist/src/tools/location-tool.js +3 -1
  15. package/dist/src/tools/login-token-tool.js +3 -1
  16. package/dist/src/tools/modify-alarm-tool.js +3 -1
  17. package/dist/src/tools/modify-note-tool.js +3 -1
  18. package/dist/src/tools/note-tool.js +3 -1
  19. package/dist/src/tools/query-app-message-tool.js +3 -1
  20. package/dist/src/tools/query-memory-data-tool.js +3 -1
  21. package/dist/src/tools/query-todo-task-tool.js +3 -1
  22. package/dist/src/tools/save-file-to-phone-tool.js +3 -1
  23. package/dist/src/tools/save-media-to-gallery-tool.js +3 -1
  24. package/dist/src/tools/save-self-evolution-skill-tool.js +3 -1
  25. package/dist/src/tools/search-alarm-tool.js +3 -1
  26. package/dist/src/tools/search-calendar-tool.js +3 -1
  27. package/dist/src/tools/search-contact-tool.js +3 -1
  28. package/dist/src/tools/search-email-tool.js +3 -1
  29. package/dist/src/tools/search-file-tool.js +3 -1
  30. package/dist/src/tools/search-message-tool.js +3 -1
  31. package/dist/src/tools/search-note-tool.js +3 -1
  32. package/dist/src/tools/search-photo-gallery-tool.js +3 -1
  33. package/dist/src/tools/send-cross-device-task-tool.js +3 -1
  34. package/dist/src/tools/send-email-tool.js +3 -1
  35. package/dist/src/tools/send-file-to-user-tool.d.ts +1 -1
  36. package/dist/src/tools/send-file-to-user-tool.js +3 -2
  37. package/dist/src/tools/send-html-card-tool.js +3 -1
  38. package/dist/src/tools/send-message-tool.js +3 -1
  39. package/dist/src/tools/session-manager.d.ts +6 -4
  40. package/dist/src/tools/session-manager.js +14 -4
  41. package/dist/src/tools/upload-file-tool.js +3 -1
  42. package/dist/src/tools/upload-photo-tool.js +3 -1
  43. package/dist/src/tools/xiaoyi-add-collection-tool.js +3 -1
  44. package/dist/src/tools/xiaoyi-collection-tool.js +3 -1
  45. package/dist/src/tools/xiaoyi-delete-collection-tool.js +3 -1
  46. package/dist/src/tools/xiaoyi-gui-tool.js +3 -2
  47. package/package.json +1 -1
@@ -5,6 +5,7 @@ import { clearRunCrossTaskSentFiles, getCurrentSessionContext } from "./tools/se
5
5
  import fs from "fs/promises";
6
6
  import path from "path";
7
7
  import { logger } from "./utils/logger.js";
8
+ import { getCurrentTaskId, getCurrentMessageId } from "./task-manager.js";
8
9
  const TEMP_FILE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
9
10
  const RUN_CROSS_TASK_LOG_TAG = "[RunCrossTask]";
10
11
  function buildDistributionStatusCommand(context) {
@@ -99,8 +100,13 @@ export function createXYReplyDispatcher(params) {
99
100
  // 初始taskId和messageId(作为fallback)
100
101
  const initialTaskId = taskId;
101
102
  const initialMessageId = messageId;
102
- const getActiveTaskId = () => initialTaskId;
103
- const getActiveMessageId = () => initialMessageId;
103
+ // 跨链读取:steer 消息通过 registerTaskId 更新 Map,这里读取最新 taskId
104
+ const getActiveTaskId = () => {
105
+ return getCurrentTaskId(sessionId) ?? initialTaskId;
106
+ };
107
+ const getActiveMessageId = () => {
108
+ return getCurrentMessageId(sessionId) ?? initialMessageId;
109
+ };
104
110
  // Create a scoped logger that always uses this session's sessionId
105
111
  // and dynamically resolves the latest taskId
106
112
  const scopedLog = () => logger.withContext(sessionId, getActiveTaskId());
@@ -129,12 +135,13 @@ export function createXYReplyDispatcher(params) {
129
135
  scopedLog().log(`[STATUS-INTERVAL] Starting interval`);
130
136
  statusUpdateInterval = setInterval(() => {
131
137
  // 🔑 使用动态taskId
132
- scopedLog().log(`[STATUS-INTERVAL] Triggering status update, taskId=${taskId}`);
138
+ const currentTaskId = getActiveTaskId();
139
+ scopedLog().log(`[STATUS-INTERVAL] Triggering status update, taskId=${currentTaskId}`);
133
140
  void sendStatusUpdate({
134
141
  config,
135
142
  sessionId,
136
- taskId, // 🔑 动态taskId
137
- messageId, // 🔑 动态messageId
143
+ taskId: currentTaskId,
144
+ messageId: getActiveMessageId(),
138
145
  text: "任务正在处理中,请稍候~",
139
146
  state: "working",
140
147
  }).catch((err) => {
@@ -184,8 +191,8 @@ export function createXYReplyDispatcher(params) {
184
191
  await sendA2AResponse({
185
192
  config,
186
193
  sessionId,
187
- taskId,
188
- messageId,
194
+ taskId: getActiveTaskId(),
195
+ messageId: getActiveMessageId(),
189
196
  text,
190
197
  append: true,
191
198
  final: false,
@@ -208,8 +215,8 @@ export function createXYReplyDispatcher(params) {
208
215
  await sendStatusUpdate({
209
216
  config,
210
217
  sessionId,
211
- taskId,
212
- messageId,
218
+ taskId: getActiveTaskId(),
219
+ messageId: getActiveMessageId(),
213
220
  text: "处理失败,请稍后重试",
214
221
  state: "failed",
215
222
  });
@@ -240,8 +247,8 @@ export function createXYReplyDispatcher(params) {
240
247
  await sendRunCrossTaskResult({
241
248
  config,
242
249
  sessionId,
243
- taskId,
244
- messageId,
250
+ taskId: getActiveTaskId(),
251
+ messageId: getActiveMessageId(),
245
252
  context: runCrossTaskContext,
246
253
  resultCode: "0",
247
254
  resultMessage: crossTaskResultMessage,
@@ -251,8 +258,8 @@ export function createXYReplyDispatcher(params) {
251
258
  await sendStatusUpdate({
252
259
  config,
253
260
  sessionId,
254
- taskId,
255
- messageId,
261
+ taskId: getActiveTaskId(),
262
+ messageId: getActiveMessageId(),
256
263
  text: "任务处理已完成~",
257
264
  state: "completed",
258
265
  });
@@ -261,8 +268,8 @@ export function createXYReplyDispatcher(params) {
261
268
  await sendA2AResponse({
262
269
  config,
263
270
  sessionId,
264
- taskId,
265
- messageId,
271
+ taskId: getActiveTaskId(),
272
+ messageId: getActiveMessageId(),
266
273
  text: "",
267
274
  append: true,
268
275
  final: true,
@@ -283,8 +290,8 @@ export function createXYReplyDispatcher(params) {
283
290
  await sendRunCrossTaskResult({
284
291
  config,
285
292
  sessionId,
286
- taskId,
287
- messageId,
293
+ taskId: getActiveTaskId(),
294
+ messageId: getActiveMessageId(),
288
295
  context: runCrossTaskContext,
289
296
  resultCode: "1",
290
297
  resultMessage: "任务执行异常,请重试",
@@ -293,8 +300,8 @@ export function createXYReplyDispatcher(params) {
293
300
  await sendStatusUpdate({
294
301
  config,
295
302
  sessionId,
296
- taskId,
297
- messageId,
303
+ taskId: getActiveTaskId(),
304
+ messageId: getActiveMessageId(),
298
305
  text: "任务处理中断了~",
299
306
  state: "failed",
300
307
  });
@@ -302,8 +309,8 @@ export function createXYReplyDispatcher(params) {
302
309
  await sendA2AResponse({
303
310
  config,
304
311
  sessionId,
305
- taskId,
306
- messageId,
312
+ taskId: getActiveTaskId(),
313
+ messageId: getActiveMessageId(),
307
314
  text: "任务执行异常,请重试~",
308
315
  append: true,
309
316
  final: true,
@@ -347,8 +354,8 @@ export function createXYReplyDispatcher(params) {
347
354
  await sendStatusUpdate({
348
355
  config,
349
356
  sessionId,
350
- taskId,
351
- messageId,
357
+ taskId: getActiveTaskId(),
358
+ messageId: getActiveMessageId(),
352
359
  text: `正在使用工具: ${toolName}...`,
353
360
  state: "working",
354
361
  });
@@ -373,8 +380,8 @@ export function createXYReplyDispatcher(params) {
373
380
  await sendStatusUpdate({
374
381
  config,
375
382
  sessionId,
376
- taskId,
377
- messageId,
383
+ taskId: getActiveTaskId(),
384
+ messageId: getActiveMessageId(),
378
385
  text: resultText,
379
386
  state: "working",
380
387
  });
@@ -401,8 +408,8 @@ export function createXYReplyDispatcher(params) {
401
408
  await sendReasoningTextUpdate({
402
409
  config,
403
410
  sessionId,
404
- taskId,
405
- messageId,
411
+ taskId: getActiveTaskId(),
412
+ messageId: getActiveMessageId(),
406
413
  text,
407
414
  append: false,
408
415
  });
@@ -425,8 +432,8 @@ export function createXYReplyDispatcher(params) {
425
432
  await sendA2AResponse({
426
433
  config,
427
434
  sessionId,
428
- taskId,
429
- messageId,
435
+ taskId: getActiveTaskId(),
436
+ messageId: getActiveMessageId(),
430
437
  text,
431
438
  append: false,
432
439
  final: false,
@@ -13,6 +13,17 @@ export declare function registerTaskId(sessionId: string, taskId: string, messag
13
13
  * 移除session的活跃taskId(消息处理完成时调用)。
14
14
  */
15
15
  export declare function decrementTaskIdRef(sessionId: string): void;
16
+ /**
17
+ * 获取session的当前活跃taskId。
18
+ * 仅供 reply-dispatcher 跨链读取 steer 更新后的 taskId。
19
+ * 工具和 sendCommand 应使用 ALS SessionContext.taskId。
20
+ */
21
+ export declare function getCurrentTaskId(sessionId: string): string | null;
22
+ /**
23
+ * 获取session的当前活跃messageId。
24
+ * 仅供 reply-dispatcher 跨链读取 steer 更新后的 messageId。
25
+ */
26
+ export declare function getCurrentMessageId(sessionId: string): string | null;
16
27
  /**
17
28
  * 检查session是否有活跃的taskId
18
29
  */
@@ -43,6 +43,23 @@ export function decrementTaskIdRef(sessionId) {
43
43
  logger.log(`[TASK_MANAGER] Removing taskId`);
44
44
  activeTaskIds.delete(sessionId);
45
45
  }
46
+ /**
47
+ * 获取session的当前活跃taskId。
48
+ * 仅供 reply-dispatcher 跨链读取 steer 更新后的 taskId。
49
+ * 工具和 sendCommand 应使用 ALS SessionContext.taskId。
50
+ */
51
+ export function getCurrentTaskId(sessionId) {
52
+ const binding = activeTaskIds.get(sessionId);
53
+ return binding?.currentTaskId ?? null;
54
+ }
55
+ /**
56
+ * 获取session的当前活跃messageId。
57
+ * 仅供 reply-dispatcher 跨链读取 steer 更新后的 messageId。
58
+ */
59
+ export function getCurrentMessageId(sessionId) {
60
+ const binding = activeTaskIds.get(sessionId);
61
+ return binding?.currentMessageId ?? null;
62
+ }
46
63
  /**
47
64
  * 检查session是否有活跃的taskId
48
65
  */
@@ -1,6 +1,7 @@
1
1
  // Agent-as-skill tool implementation - invokes another agent as a skill
2
2
  import { getXYWebSocketManager } from "../client.js";
3
3
  import { sendCommand } from "../formatter.js";
4
+ import { getCurrentSessionContext } from './session-manager.js';
4
5
  import { logger } from "../utils/logger.js";
5
6
  import { XYFileUploadService } from "../file-upload.js";
6
7
  /**
@@ -9,7 +10,6 @@ import { XYFileUploadService } from "../file-upload.js";
9
10
  * forwards the request to the target agent via WebSocket, and returns the result.
10
11
  */
11
12
  export function createAgentAsSkillTool(ctx) {
12
- const { config, sessionId, taskId, messageId } = ctx;
13
13
  return {
14
14
  name: "agent_as_a_tool",
15
15
  label: "Agent as Skill Tool",
@@ -65,6 +65,8 @@ export function createAgentAsSkillTool(ctx) {
65
65
  required: ["agentId", "query"],
66
66
  },
67
67
  async execute(toolCallId, params) {
68
+ const _c = getCurrentSessionContext() ?? ctx;
69
+ const { config, sessionId, taskId, messageId } = _c;
68
70
  // Dynamic lookup: use latest taskId from task-manager (handles steer/interrupt)
69
71
  // Validate parameters
70
72
  if (!params.agentId || typeof params.agentId !== "string") {
@@ -1,5 +1,6 @@
1
1
  import { getXYWebSocketManager } from "../client.js";
2
2
  import { sendCommand } from "../formatter.js";
3
+ import { getCurrentSessionContext } from './session-manager.js';
3
4
  import { logger } from "../utils/logger.js";
4
5
  /**
5
6
  * XY calendar event tool - creates a calendar event on user's device.
@@ -7,7 +8,6 @@ import { logger } from "../utils/logger.js";
7
8
  * Time format must be: yyyy-mm-dd hh:mm:ss
8
9
  */
9
10
  export function createCalendarTool(ctx) {
10
- const { config, sessionId, taskId, messageId } = ctx;
11
11
  return {
12
12
  name: "create_calendar_event",
13
13
  label: "Create Calendar Event",
@@ -35,6 +35,8 @@ export function createCalendarTool(ctx) {
35
35
  required: ["title", "dtStart", "dtEnd"],
36
36
  },
37
37
  async execute(toolCallId, params) {
38
+ const _c = getCurrentSessionContext() ?? ctx;
39
+ const { config, sessionId, taskId, messageId } = _c;
38
40
  // Validate parameters
39
41
  if (!params.title || !params.dtStart || !params.dtEnd) {
40
42
  throw new Error("Missing required parameters: title, dtStart, and dtEnd are required");
@@ -23,12 +23,12 @@ import { createSaveFileToPhoneTool } from "./save-file-to-phone-tool.js";
23
23
  import { createSendEmailTool } from "./send-email-tool.js";
24
24
  import { createSearchEmailTool } from "./search-email-tool.js";
25
25
  import { sendStatusUpdate } from "../formatter.js";
26
+ import { getCurrentSessionContext } from './session-manager.js';
26
27
  /**
27
28
  * call_device_tool - 通用端工具调度器。
28
29
  * LLM 必须先通过 get_xxx_tool_schema 获取具体工具 schema,再用本工具执行。
29
30
  */
30
31
  export function createCallDeviceTool(ctx) {
31
- const { config, sessionId, taskId, messageId } = ctx;
32
32
  const noteTool = createNoteTool(ctx);
33
33
  const modifyNoteTool = createModifyNoteTool(ctx);
34
34
  const createAlarmTool = makeAlarmTool(ctx);
@@ -101,6 +101,8 @@ export function createCallDeviceTool(ctx) {
101
101
  required: ["toolName", "arguments"],
102
102
  },
103
103
  async execute(toolCallId, params) {
104
+ const _c = getCurrentSessionContext() ?? ctx;
105
+ const { config, sessionId, taskId, messageId } = _c;
104
106
  const { toolName, arguments: toolArgs } = params;
105
107
  // 向用户端发送具体工具名的状态更新
106
108
  try {
@@ -1,12 +1,12 @@
1
1
  import { getXYWebSocketManager } from "../client.js";
2
2
  import { sendCommand } from "../formatter.js";
3
+ import { getCurrentSessionContext } from './session-manager.js';
3
4
  import { logger } from "../utils/logger.js";
4
5
  /**
5
6
  * XY call phone tool - makes a phone call on user's device.
6
7
  * Requires phoneNumber parameter and optional slotId (0 for primary SIM, 1 for secondary SIM).
7
8
  */
8
9
  export function createCallPhoneTool(ctx) {
9
- const { config, sessionId, taskId, messageId } = ctx;
10
10
  return {
11
11
  name: "call_phone",
12
12
  label: "Call Phone",
@@ -27,6 +27,8 @@ export function createCallPhoneTool(ctx) {
27
27
  required: ["phoneNumber"],
28
28
  },
29
29
  async execute(toolCallId, params) {
30
+ const _c = getCurrentSessionContext() ?? ctx;
31
+ const { config, sessionId, taskId, messageId } = _c;
30
32
  // Validate phoneNumber parameter
31
33
  if (!params.phoneNumber || typeof params.phoneNumber !== "string" || params.phoneNumber.trim() === "") {
32
34
  throw new Error("Missing required parameter: phoneNumber must be a non-empty string");
@@ -1,6 +1,7 @@
1
1
  // Check plugin privilege tool implementation
2
2
  import { getXYWebSocketManager } from "../client.js";
3
3
  import { sendCommand } from "../formatter.js";
4
+ import { getCurrentSessionContext } from './session-manager.js';
4
5
  import { logger } from "../utils/logger.js";
5
6
  /**
6
7
  * Mapping from intent name to required permission IDs.
@@ -32,7 +33,6 @@ const INTENT_PERMISSION_MAP = {
32
33
  * used in scheduled tasks.
33
34
  */
34
35
  export function createCheckPluginPrivilegeTool(ctx) {
35
- const { config, sessionId, taskId, messageId } = ctx;
36
36
  return {
37
37
  name: "check_plugin_privilege",
38
38
  label: "Check Plugin Privilege",
@@ -75,6 +75,8 @@ export function createCheckPluginPrivilegeTool(ctx) {
75
75
  required: ["checkIntentName"],
76
76
  },
77
77
  async execute(toolCallId, params) {
78
+ const _c = getCurrentSessionContext() ?? ctx;
79
+ const { config, sessionId, taskId, messageId } = _c;
78
80
  const { checkIntentName } = params;
79
81
  // Look up permission IDs for the given intent name
80
82
  const permissionId = INTENT_PERMISSION_MAP[checkIntentName];
@@ -1,5 +1,6 @@
1
1
  import { getXYWebSocketManager } from "../client.js";
2
2
  import { sendCommand } from "../formatter.js";
3
+ import { getCurrentSessionContext } from './session-manager.js';
3
4
  import { logger } from "../utils/logger.js";
4
5
  // Enum definitions for alarm parameters
5
6
  const ALARM_SNOOZE_DURATION_VALUES = [5, 10, 15, 20, 25, 30];
@@ -14,7 +15,6 @@ const DAYS_OF_WEEK_VALUES = ["Mon", "Tues", "Wed", "Thur", "Fri", "Sat", "Sun"];
14
15
  * Time format: YYYYMMDD hhmmss (e.g., 20240315 143000)
15
16
  */
16
17
  export function makeAlarmTool(ctx) {
17
- const { config, sessionId, taskId, messageId } = ctx;
18
18
  return {
19
19
  name: "create_alarm",
20
20
  label: "Create Alarm",
@@ -61,6 +61,8 @@ b. 使用该工具之前需获取当前真实时间
61
61
  required: ["alarmTime"],
62
62
  },
63
63
  async execute(toolCallId, params) {
64
+ const _c = getCurrentSessionContext() ?? ctx;
65
+ const { config, sessionId, taskId, messageId } = _c;
64
66
  // ===== Validate required parameter: alarmTime =====
65
67
  if (!params.alarmTime || typeof params.alarmTime !== "string") {
66
68
  throw new Error("Missing required parameter: alarmTime must be a string in format YYYYMMDD hhmmss");
@@ -1,5 +1,6 @@
1
1
  import { getXYWebSocketManager } from "../client.js";
2
2
  import { sendCommand } from "../formatter.js";
3
+ import { getCurrentSessionContext } from './session-manager.js';
3
4
  import { logger } from "../utils/logger.js";
4
5
  /**
5
6
  * XY delete alarm tool - deletes existing alarms on user's device.
@@ -12,7 +13,6 @@ import { logger } from "../utils/logger.js";
12
13
  * Supports deleting single or multiple alarms in one call.
13
14
  */
14
15
  export function createDeleteAlarmTool(ctx) {
15
- const { config, sessionId, taskId, messageId } = ctx;
16
16
  return {
17
17
  name: "delete_alarm",
18
18
  label: "Delete Alarm",
@@ -40,6 +40,8 @@ export function createDeleteAlarmTool(ctx) {
40
40
  required: ["items"],
41
41
  },
42
42
  async execute(toolCallId, params) {
43
+ const _c = getCurrentSessionContext() ?? ctx;
44
+ const { config, sessionId, taskId, messageId } = _c;
43
45
  // ===== 参数规范化:兼容数组和 JSON 字符串 =====
44
46
  let items = null;
45
47
  if (!params.items) {
@@ -1,5 +1,6 @@
1
1
  import { sendCommand, sendStatusUpdate } from "../formatter.js";
2
2
  import { getXYWebSocketManager } from "../client.js";
3
+ import { getCurrentSessionContext } from './session-manager.js';
3
4
  import { logger } from "../utils/logger.js";
4
5
  const DISCOVER_DEVICES_INTENT = "SearchAllDeviceInfo";
5
6
  const DISCOVER_DEVICES_BUNDLE = "com.huawei.hmos.vassistant";
@@ -97,7 +98,6 @@ function recommendDevices(query, devices) {
97
98
  };
98
99
  }
99
100
  export function createDiscoverCrossDevicesTool(ctx) {
100
- const { config, sessionId, taskId, messageId } = ctx;
101
101
  return {
102
102
  name: "discover_cross_devices",
103
103
  label: "发现跨设备协作设备",
@@ -117,6 +117,8 @@ export function createDiscoverCrossDevicesTool(ctx) {
117
117
  required: ["query"],
118
118
  },
119
119
  async execute(_toolCallId, params) {
120
+ const _c = getCurrentSessionContext() ?? ctx;
121
+ const { config, sessionId, taskId, messageId } = _c;
120
122
  const query = typeof params.query === "string" ? params.query.trim() : "";
121
123
  logger.log(`${LOG_TAG} tool invoked`);
122
124
  if (!query) {
@@ -1,5 +1,6 @@
1
1
  import { sendCommand } from "../formatter.js";
2
2
  import { logger } from "../utils/logger.js";
3
+ import { getCurrentSessionContext } from './session-manager.js';
3
4
  class ToolInputError extends Error {
4
5
  status = 400;
5
6
  constructor(message) {
@@ -11,7 +12,6 @@ function isJsonObjectOrArray(value) {
11
12
  return !!value && typeof value === "object";
12
13
  }
13
14
  export function createDisplayA2UICardTool(ctx) {
14
- const { config, sessionId, taskId, messageId } = ctx;
15
15
  return {
16
16
  name: "displayA2UICard",
17
17
  label: "Display A2UI Card",
@@ -34,6 +34,8 @@ export function createDisplayA2UICardTool(ctx) {
34
34
  required: ["cardId", "cardData"],
35
35
  },
36
36
  async execute(toolCallId, params) {
37
+ const _c = getCurrentSessionContext() ?? ctx;
38
+ const { config, sessionId, taskId, messageId } = _c;
37
39
  const cardId = typeof params?.cardId === "string" ? params.cardId.trim() : "";
38
40
  const cardData = params?.cardData;
39
41
  if (!cardId) {
@@ -1,6 +1,7 @@
1
1
  // Image Reading tool implementation
2
2
  import { createHash } from "crypto";
3
3
  import { XYFileUploadService } from "../file-upload.js";
4
+ import { getCurrentSessionContext } from './session-manager.js';
4
5
  import fetch from "node-fetch";
5
6
  import fs from "fs/promises";
6
7
  import { v4 as uuidv4 } from "uuid";
@@ -166,7 +167,6 @@ async function callImageUnderstandingAPI(imageUrls, text, apiKey, uid, fileUploa
166
167
  * Supports both local file paths and remote URLs, up to 10 images at once.
167
168
  */
168
169
  export function createImageReadingTool(ctx) {
169
- const { config, sessionId } = ctx;
170
170
  return {
171
171
  name: "image_reading",
172
172
  label: "Image Reading",
@@ -187,6 +187,8 @@ export function createImageReadingTool(ctx) {
187
187
  required: ["images"],
188
188
  },
189
189
  async execute(toolCallId, params) {
190
+ const _c = getCurrentSessionContext() ?? ctx;
191
+ const { config, sessionId } = _c;
190
192
  // Normalize images param
191
193
  const images = params.images
192
194
  ? (Array.isArray(params.images) ? params.images : [params.images])
@@ -1,12 +1,12 @@
1
1
  import { getXYWebSocketManager } from "../client.js";
2
2
  import { sendCommand } from "../formatter.js";
3
+ import { getCurrentSessionContext } from './session-manager.js';
3
4
  import { logger } from "../utils/logger.js";
4
5
  /**
5
6
  * XY location tool - gets user's current location.
6
7
  * Returns WGS84 coordinates (latitude, longitude).
7
8
  */
8
9
  export function createLocationTool(ctx) {
9
- const { config, sessionId, taskId, messageId } = ctx;
10
10
  return {
11
11
  name: "get_user_location",
12
12
  label: "Get User Location",
@@ -17,6 +17,8 @@ export function createLocationTool(ctx) {
17
17
  required: [],
18
18
  },
19
19
  async execute(toolCallId, params) {
20
+ const _c = getCurrentSessionContext() ?? ctx;
21
+ const { config, sessionId, taskId, messageId } = _c;
20
22
  // Get WebSocket manager
21
23
  const wsManager = getXYWebSocketManager(config);
22
24
  // Build GetCurrentLocation command
@@ -1,6 +1,7 @@
1
1
  // Login Token tool - 自动获取用户授权信息
2
2
  import { v4 as uuidv4 } from "uuid";
3
3
  import { getXYWebSocketManager } from "../client.js";
4
+ import { getCurrentSessionContext } from './session-manager.js';
4
5
  import { readFileSync, existsSync } from "fs";
5
6
  import { logger } from "../utils/logger.js";
6
7
  const TOKEN_FILE_PATH = "/home/sandbox/.openclaw/.xiaoyitoken.json";
@@ -12,7 +13,6 @@ const TOKEN_VALIDITY_MS = 5 * 60 * 1000; // 5 minutes
12
13
  * 当 skill 依赖用户获取鉴权信息时,此工具协助用户快速获取鉴权信息。
13
14
  */
14
15
  export function createLoginTokenTool(ctx) {
15
- const { config, sessionId, taskId, messageId } = ctx;
16
16
  return {
17
17
  name: "huawei_id_tool",
18
18
  label: "Get Login Token",
@@ -32,6 +32,8 @@ export function createLoginTokenTool(ctx) {
32
32
  required: ["clientId", "skillName"],
33
33
  },
34
34
  async execute(toolCallId, params) {
35
+ const _c = getCurrentSessionContext() ?? ctx;
36
+ const { config, sessionId, taskId, messageId } = _c;
35
37
  const { clientId, skillName } = params;
36
38
  if (!clientId || typeof clientId !== "string" || clientId.trim() === "") {
37
39
  throw new Error("Missing required parameter: clientId must be a non-empty string");
@@ -1,5 +1,6 @@
1
1
  import { getXYWebSocketManager } from "../client.js";
2
2
  import { sendCommand } from "../formatter.js";
3
+ import { getCurrentSessionContext } from './session-manager.js';
3
4
  import { logger } from "../utils/logger.js";
4
5
  // Enum definitions for alarm parameters (same as create-alarm-tool)
5
6
  const ALARM_SNOOZE_DURATION_VALUES = [5, 10, 15, 20, 25, 30];
@@ -16,7 +17,6 @@ const DAYS_OF_WEEK_VALUES = ["Mon", "Tues", "Wed", "Thur", "Fri", "Sat", "Sun"];
16
17
  * 2. Use the entityId to identify which alarm to modify
17
18
  */
18
19
  export function createModifyAlarmTool(ctx) {
19
- const { config, sessionId, taskId, messageId } = ctx;
20
20
  return {
21
21
  name: "modify_alarm",
22
22
  label: "Modify Alarm",
@@ -74,6 +74,8 @@ export function createModifyAlarmTool(ctx) {
74
74
  required: ["entityId"],
75
75
  },
76
76
  async execute(toolCallId, params) {
77
+ const _c = getCurrentSessionContext() ?? ctx;
78
+ const { config, sessionId, taskId, messageId } = _c;
77
79
  // Coerce numeric string params to actual numbers
78
80
  // The model may produce "1" instead of 1, which would fail typeof checks
79
81
  const numericParams = [
@@ -1,5 +1,6 @@
1
1
  import { getXYWebSocketManager } from "../client.js";
2
2
  import { sendCommand } from "../formatter.js";
3
+ import { getCurrentSessionContext } from './session-manager.js';
3
4
  import { logger } from "../utils/logger.js";
4
5
  /**
5
6
  * XY modify note tool - appends content to an existing note on user's device.
@@ -10,7 +11,6 @@ import { logger } from "../utils/logger.js";
10
11
  * 2. Use the entityId to append content to that note
11
12
  */
12
13
  export function createModifyNoteTool(ctx) {
13
- const { config, sessionId, taskId, messageId } = ctx;
14
14
  return {
15
15
  name: "modify_note",
16
16
  label: "Modify Note",
@@ -30,6 +30,8 @@ export function createModifyNoteTool(ctx) {
30
30
  required: ["entityId", "text"],
31
31
  },
32
32
  async execute(toolCallId, params) {
33
+ const _c = getCurrentSessionContext() ?? ctx;
34
+ const { config, sessionId, taskId, messageId } = _c;
33
35
  // Validate parameters
34
36
  if (!params.entityId || !params.text) {
35
37
  throw new Error("Missing required parameters: entityId and text are required");
@@ -1,5 +1,6 @@
1
1
  import { getXYWebSocketManager } from "../client.js";
2
2
  import { sendCommand } from "../formatter.js";
3
+ import { getCurrentSessionContext } from './session-manager.js';
3
4
  import { logger } from "../utils/logger.js";
4
5
  /**
5
6
  * Duck-typed ToolInputError: openclaw 按 .name 字段匹配,不用 instanceof。
@@ -18,7 +19,6 @@ class ToolInputError extends Error {
18
19
  * Requires title and content parameters.
19
20
  */
20
21
  export function createNoteTool(ctx) {
21
- const { config, sessionId, taskId, messageId } = ctx;
22
22
  return {
23
23
  name: "create_note",
24
24
  label: "Create Note",
@@ -45,6 +45,8 @@ export function createNoteTool(ctx) {
45
45
  required: ["title", "content"],
46
46
  },
47
47
  async execute(toolCallId, params) {
48
+ const _c = getCurrentSessionContext() ?? ctx;
49
+ const { config, sessionId, taskId, messageId } = _c;
48
50
  // Validate parameters — 抛 ToolInputError 而非普通 Error,
49
51
  // 让 openclaw 返回 400 而非 500,明确告知 LLM 这是参数错误,不应重试。
50
52
  if (typeof params.title !== "string" || !params.title) {
@@ -1,6 +1,7 @@
1
1
  // QueryAppMessage tool implementation
2
2
  import { getXYWebSocketManager } from "../client.js";
3
3
  import { sendCommand } from "../formatter.js";
4
+ import { getCurrentSessionContext } from './session-manager.js';
4
5
  import { logger } from "../utils/logger.js";
5
6
  class ToolInputError extends Error {
6
7
  status = 400;
@@ -13,7 +14,6 @@ class ToolInputError extends Error {
13
14
  * 查询指定时间范围内的设备通知消息。
14
15
  */
15
16
  export function createQueryAppMessageTool(ctx) {
16
- const { config, sessionId, taskId, messageId } = ctx;
17
17
  return {
18
18
  name: "query_app_message",
19
19
  label: "Query App Message",
@@ -47,6 +47,8 @@ c. 调用工具前需认真检查调用参数是否满足工具要求
47
47
  required: [],
48
48
  },
49
49
  async execute(toolCallId, params) {
50
+ const _c = getCurrentSessionContext() ?? ctx;
51
+ const { config, sessionId, taskId, messageId } = _c;
50
52
  const wsManager = getXYWebSocketManager(config);
51
53
  const intentParam = {};
52
54
  if (params.startTime !== undefined)
@@ -1,6 +1,7 @@
1
1
  // QueryMemoryData tool implementation
2
2
  import { getXYWebSocketManager } from "../client.js";
3
3
  import { sendCommand } from "../formatter.js";
4
+ import { getCurrentSessionContext } from './session-manager.js';
4
5
  import { logger } from "../utils/logger.js";
5
6
  class ToolInputError extends Error {
6
7
  status = 400;
@@ -27,7 +28,6 @@ const VALID_SUB_CATEGORIES = {
27
28
  * 查询存储在设备本地的结构化记忆数据。
28
29
  */
29
30
  export function createQueryMemoryDataTool(ctx) {
30
- const { config, sessionId, taskId, messageId } = ctx;
31
31
  return {
32
32
  name: "query_memory_data",
33
33
  label: "Query Memory Data",
@@ -56,6 +56,8 @@ c. 调用工具前需认真检查调用参数是否满足工具要求
56
56
  required: [],
57
57
  },
58
58
  async execute(toolCallId, params) {
59
+ const _c = getCurrentSessionContext() ?? ctx;
60
+ const { config, sessionId, taskId, messageId } = _c;
59
61
  const { category, subCategory } = params;
60
62
  // Validate category
61
63
  if (category && !VALID_CATEGORIES.includes(category)) {
@@ -1,6 +1,7 @@
1
1
  // QueryTodoTask tool implementation
2
2
  import { getXYWebSocketManager } from "../client.js";
3
3
  import { sendCommand } from "../formatter.js";
4
+ import { getCurrentSessionContext } from './session-manager.js';
4
5
  import { logger } from "../utils/logger.js";
5
6
  class ToolInputError extends Error {
6
7
  status = 400;
@@ -13,7 +14,6 @@ class ToolInputError extends Error {
13
14
  * 获取指定时间范围内的全局待办任务列表。
14
15
  */
15
16
  export function createQueryTodoTaskTool(ctx) {
16
- const { config, sessionId, taskId, messageId } = ctx;
17
17
  return {
18
18
  name: "query_todo_task",
19
19
  label: "Query Todo Task",
@@ -44,6 +44,8 @@ d. 当只传入 startTime 时,返回该时间点之后的所有任务;当只
44
44
  required: [],
45
45
  },
46
46
  async execute(toolCallId, params) {
47
+ const _c = getCurrentSessionContext() ?? ctx;
48
+ const { config, sessionId, taskId, messageId } = _c;
47
49
  const { status } = params;
48
50
  if (status && !["all", "completed", "pending"].includes(status)) {
49
51
  throw new ToolInputError('status 参数只能为 "all"、"completed" 或 "pending"');