@ynhcj/xiaoyi-channel 0.0.151-beta → 0.0.151-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 (76) hide show
  1. package/dist/index.js +23 -0
  2. package/dist/src/bot.js +9 -1
  3. package/dist/src/channel.js +59 -5
  4. package/dist/src/cron-command.d.ts +15 -0
  5. package/dist/src/cron-command.js +49 -0
  6. package/dist/src/cron-query-handler.d.ts +7 -0
  7. package/dist/src/cron-query-handler.js +188 -0
  8. package/dist/src/cspl/call_api.d.ts +1 -1
  9. package/dist/src/cspl/call_api.js +2 -2
  10. package/dist/src/cspl/config.js +30 -10
  11. package/dist/src/cspl/constants.d.ts +3 -0
  12. package/dist/src/cspl/constants.js +5 -0
  13. package/dist/src/cspl/sentinel_hook.js +17 -3
  14. package/dist/src/cspl/utils.js +2 -2
  15. package/dist/src/formatter.d.ts +14 -1
  16. package/dist/src/formatter.js +31 -8
  17. package/dist/src/monitor.js +13 -1
  18. package/dist/src/parser.d.ts +2 -1
  19. package/dist/src/parser.js +55 -0
  20. package/dist/src/provider.js +19 -17
  21. package/dist/src/push.d.ts +11 -1
  22. package/dist/src/push.js +93 -2
  23. package/dist/src/reply-dispatcher.js +113 -14
  24. package/dist/src/self-evolution-handler.js +1 -1
  25. package/dist/src/tools/agent-as-skill-tool.js +56 -4
  26. package/dist/src/tools/calendar-tool.js +2 -1
  27. package/dist/src/tools/call-device-tool.js +0 -3
  28. package/dist/src/tools/call-phone-tool.js +2 -1
  29. package/dist/src/tools/create-alarm-tool.js +2 -1
  30. package/dist/src/tools/create-all-tools.js +8 -4
  31. package/dist/src/tools/delete-alarm-tool.js +2 -1
  32. package/dist/src/tools/device-tool-map.d.ts +1 -1
  33. package/dist/src/tools/device-tool-map.js +12 -5
  34. package/dist/src/tools/discover-cross-devices-tool.d.ts +2 -0
  35. package/dist/src/tools/discover-cross-devices-tool.js +235 -0
  36. package/dist/src/tools/display-a2ui-card-tool.d.ts +2 -0
  37. package/dist/src/tools/display-a2ui-card-tool.js +85 -0
  38. package/dist/src/tools/find-pc-devices-tool.js +1 -0
  39. package/dist/src/tools/get-collection-tool-schema.js +1 -1
  40. package/dist/src/tools/get-device-file-tool-schema.js +2 -3
  41. package/dist/src/tools/location-tool.js +2 -1
  42. package/dist/src/tools/modify-alarm-tool.js +2 -1
  43. package/dist/src/tools/modify-note-tool.js +2 -1
  44. package/dist/src/tools/note-tool.js +2 -1
  45. package/dist/src/tools/query-app-message-tool.js +4 -3
  46. package/dist/src/tools/query-memory-data-tool.js +4 -3
  47. package/dist/src/tools/query-todo-task-tool.js +4 -3
  48. package/dist/src/tools/save-file-to-phone-tool.js +2 -1
  49. package/dist/src/tools/save-media-to-gallery-tool.js +2 -1
  50. package/dist/src/tools/schema-tool-factory.js +1 -1
  51. package/dist/src/tools/search-alarm-tool.js +2 -1
  52. package/dist/src/tools/search-calendar-tool.js +2 -1
  53. package/dist/src/tools/search-contact-tool.js +2 -1
  54. package/dist/src/tools/search-email-tool.js +4 -3
  55. package/dist/src/tools/search-file-tool.js +6 -10
  56. package/dist/src/tools/search-message-tool.js +1 -0
  57. package/dist/src/tools/search-note-tool.js +2 -1
  58. package/dist/src/tools/search-photo-gallery-tool.js +4 -3
  59. package/dist/src/tools/send-cross-device-task-tool.d.ts +2 -0
  60. package/dist/src/tools/send-cross-device-task-tool.js +299 -0
  61. package/dist/src/tools/send-email-tool.js +4 -3
  62. package/dist/src/tools/send-file-to-user-tool.d.ts +1 -1
  63. package/dist/src/tools/send-file-to-user-tool.js +35 -6
  64. package/dist/src/tools/send-message-tool.js +1 -0
  65. package/dist/src/tools/session-manager.d.ts +14 -1
  66. package/dist/src/tools/session-manager.js +73 -0
  67. package/dist/src/tools/upload-file-tool.js +6 -14
  68. package/dist/src/tools/upload-photo-tool.js +4 -3
  69. package/dist/src/tools/xiaoyi-add-collection-tool.js +4 -2
  70. package/dist/src/tools/xiaoyi-collection-tool.js +3 -2
  71. package/dist/src/tools/xiaoyi-delete-collection-tool.js +3 -2
  72. package/dist/src/tools/xiaoyi-gui-tool.js +6 -0
  73. package/dist/src/types.d.ts +22 -0
  74. package/dist/src/websocket.d.ts +3 -0
  75. package/dist/src/websocket.js +207 -15
  76. package/package.json +1 -1
@@ -23,7 +23,7 @@ export function createXiaoyiAddCollectionTool(ctx) {
23
23
  return {
24
24
  name: "add_collection",
25
25
  label: "Add XiaoYi Collection",
26
- description: `向小艺收藏中添加公共知识数据,可以给用户提供个性化体验。任何用户希望保存到个人化知识库中的数据都可以调用本技能。不同类型的数据对应的数据要求如下:
26
+ description: `向小艺收藏(也叫小艺帮记)中添加公共知识数据,可以给用户提供个性化体验。任何用户希望保存到个人化知识库中的数据都可以调用本技能。不同类型的数据对应的数据要求如下:
27
27
  请求入参说明:
28
28
  ● content:必填字段,数据类型为string,功能描述是该字段是用户添加收藏的链接url或文本原文。适用于HYPER_LINK和TEXT类型。
29
29
  ● uri:必填字段,数据类型为string,功能描述是该字段是图片或文件的端存储地址链接。适用于IMAGE和FILE类型。
@@ -32,6 +32,7 @@ export function createXiaoyiAddCollectionTool(ctx) {
32
32
  ● title:非必填字段,数据类型为string,功能描述是标识文件类型数据的文件名称。适用于FILE类型。
33
33
  说明:如果dataType为HYPER_LINK或TEXT,则content字段必填且不能为空;如果dataType为IMAGE或FILE,则uri字段必填且不能为空。当用户希望收藏海报、截图等图片类数据时,请将数据以图片IMAGE的形式存入到小艺帮记;当用户希望收藏电子书、笔记、报告、素材、文档、合同、协议、简历、证书、报表、日志、安装包、压缩包等描述的文件时,请将数据以文件FILE的形式存入到小艺帮记。
34
34
  当你成功收藏这个数据到小艺帮记后,请在最后显示"已成功把数据添加到[小艺帮记](vassistant://voice/main?page=CollectionPage&jumpHomePageTab=myCollection)",
35
+ 请额外注意,如果当前用户的设备是鸿蒙PC,则最后显示的内容需要额外定制,内容是"已成功把数据添加到[小艺帮记](vassistant://voice/main?agentId=celia_collection_share&agentPage=CollectionPage&from=push&action=openFlashCardPage)"
35
36
  注意:
36
37
  a. 操作超时时间为60秒,请勿重复调用此工具
37
38
  b. 如果遇到各类调用失败场景,最多只能重试一次,不可以重复调用多次。
@@ -164,7 +165,7 @@ export function createXiaoyiAddCollectionTool(ctx) {
164
165
  });
165
166
  }
166
167
  else {
167
- reject(new Error(`添加小艺收藏失败: ${event.status}`));
168
+ reject(new Error(`添加小艺收藏失败: ${JSON.stringify(event.outputs)}`));
168
169
  }
169
170
  }
170
171
  };
@@ -178,6 +179,7 @@ export function createXiaoyiAddCollectionTool(ctx) {
178
179
  taskId: currentTaskId,
179
180
  messageId,
180
181
  command,
182
+ toolCallId,
181
183
  })
182
184
  .then(() => {
183
185
  })
@@ -23,7 +23,7 @@ export function createXiaoyiCollectionTool(ctx) {
23
23
  return {
24
24
  name: "query_collection",
25
25
  label: "XiaoYi Collection",
26
- description: `检索用户在小艺收藏中记下来的公共知识数据,本技能支持查询用户收藏的公共知识数据,也可以根据特定语义化描述进行特定内容的检索,通过参数进行控制。本技能返回结果中,linkTitle是收藏内容的标题,description是对收藏内容的总结,label是收藏内容的标签,linkUrl是可以直接访问的原始内容链接。如果你认为某条数据对用户交互有用,可以通过linkUrl抓取更加丰富的原始数据。
26
+ description: `检索用户在小艺收藏(也叫小艺帮记)中记下来的公共知识数据,本技能支持查询用户收藏的公共知识数据,也可以根据特定语义化描述进行特定内容的检索,通过参数进行控制。本技能返回结果中,linkTitle是收藏内容的标题,description是对收藏内容的总结,label是收藏内容的标签,linkUrl是可以直接访问的原始内容链接。如果你认为某条数据对用户交互有用,可以通过linkUrl抓取更加丰富的原始数据。
27
27
  注意:
28
28
  a. 操作超时时间为60秒,请勿重复调用此工具
29
29
  b. 如果遇到各类调用失败场景,最多只能重试一次,不可以重复调用多次。
@@ -120,7 +120,7 @@ export function createXiaoyiCollectionTool(ctx) {
120
120
  });
121
121
  }
122
122
  else {
123
- reject(new Error(`查询小艺收藏失败: ${event.status}`));
123
+ reject(new Error(`查询小艺收藏失败: ${JSON.stringify(event.outputs)}`));
124
124
  }
125
125
  }
126
126
  };
@@ -134,6 +134,7 @@ export function createXiaoyiCollectionTool(ctx) {
134
134
  taskId: currentTaskId,
135
135
  messageId,
136
136
  command,
137
+ toolCallId,
137
138
  })
138
139
  .then(() => {
139
140
  })
@@ -22,7 +22,7 @@ export function createXiaoyiDeleteCollectionTool(ctx) {
22
22
  return {
23
23
  name: "delete_collection",
24
24
  label: "Delete XiaoYi Collection",
25
- description: `从小艺收藏中删除之前已保存的公共知识数据。任何用户希望删除已保存到个人知识库的数据都可以调用本技能。如果用户想更新之前的收藏数据,需要先query获取itemId然后再delete,最后执行Add,按照这个步骤完成收藏数据更新。
25
+ description: `从小艺收藏(也叫小艺帮记)中删除之前已保存的公共知识数据。任何用户希望删除已保存到个人知识库的数据都可以调用本技能。如果用户想更新之前的收藏数据,需要先query获取itemId然后再delete,最后执行Add,按照这个步骤完成收藏数据更新。
26
26
  注意:
27
27
  a. 操作超时时间为60秒,请勿重复调用此工具
28
28
  b. 如果遇到各类调用失败场景,最多只能重试一次,不可以重复调用多次。
@@ -135,7 +135,7 @@ export function createXiaoyiDeleteCollectionTool(ctx) {
135
135
  });
136
136
  }
137
137
  else {
138
- reject(new Error(`删除小艺收藏失败: ${event.status}`));
138
+ reject(new Error(`删除小艺收藏失败: ${JSON.stringify(event.outputs)}`));
139
139
  }
140
140
  }
141
141
  };
@@ -149,6 +149,7 @@ export function createXiaoyiDeleteCollectionTool(ctx) {
149
149
  taskId: currentTaskId,
150
150
  messageId,
151
151
  command,
152
+ toolCallId,
152
153
  })
153
154
  .then(() => {
154
155
  })
@@ -2,6 +2,7 @@
2
2
  import { getXYWebSocketManager } from "../client.js";
3
3
  import { sendCommand } from "../formatter.js";
4
4
  import { getCurrentTaskId } from "../task-manager.js";
5
+ import { isCronToolCall } from "./session-manager.js";
5
6
  import { logger } from "../utils/logger.js";
6
7
  /**
7
8
  * XiaoYi GUI tool - executes phone app interactions through GUI agent.
@@ -46,6 +47,10 @@ export function createXiaoyiGuiTool(ctx) {
46
47
  if (!params.query || typeof params.query !== "string") {
47
48
  throw new Error("Missing or invalid required parameter: query must be a non-empty string");
48
49
  }
50
+ // GUI agent only works in active WebSocket sessions — reject cron-triggered calls
51
+ if (sessionId.startsWith("cron-") || isCronToolCall(toolCallId)) {
52
+ throw new Error("xiaoyi_gui_agent 不支持定时任务触发,只能在活跃对话中使用");
53
+ }
49
54
  // Get WebSocket manager
50
55
  const wsManager = getXYWebSocketManager(config);
51
56
  // Build InvokeJarvisGUIAgentRequest command
@@ -106,6 +111,7 @@ export function createXiaoyiGuiTool(ctx) {
106
111
  taskId: currentTaskId,
107
112
  messageId,
108
113
  command,
114
+ toolCallId,
109
115
  }).then(() => {
110
116
  }).catch((error) => {
111
117
  clearTimeout(timeout);
@@ -62,6 +62,28 @@ export interface A2ADataEvent {
62
62
  outputs: Record<string, any>;
63
63
  status: "success" | "failed";
64
64
  }
65
+ export interface CrossDeviceTaskResultEvent {
66
+ sessionId: string;
67
+ code: string;
68
+ message: string;
69
+ sentFiles: SentFileParams[];
70
+ status: "success" | "failed";
71
+ rawEvent: any;
72
+ }
73
+ export interface RunCrossTaskContext {
74
+ agentId: string;
75
+ sessionId: string;
76
+ isDistributed: boolean;
77
+ networkId: string;
78
+ isSupportAgent: boolean;
79
+ sentFiles: SentFileParams[];
80
+ rawContext: any;
81
+ }
82
+ export interface SentFileParams {
83
+ fileLocalUrls?: string[];
84
+ fileRemoteUrls?: string[];
85
+ fileNames?: string[];
86
+ }
65
87
  export interface A2ATaskArtifactUpdateEvent {
66
88
  taskId: string;
67
89
  kind: "artifact-update";
@@ -102,6 +102,9 @@ export declare class XYWebSocketManager extends EventEmitter {
102
102
  * Start heartbeat.
103
103
  */
104
104
  private startHeartbeat;
105
+ private toUploadExeDataEvent;
106
+ private toCrossDeviceTaskResultEvent;
107
+ private toRunCrossTaskA2ARequest;
105
108
  /**
106
109
  * Handle incoming message from server.
107
110
  */
@@ -5,6 +5,9 @@ import { EventEmitter } from "events";
5
5
  import { logger } from "./utils/logger.js";
6
6
  import { HeartbeatManager } from "./heartbeat.js";
7
7
  import { MessageQueue } from "./message-queue.js";
8
+ import { v4 as uuidv4 } from "uuid";
9
+ const RUN_CROSS_TASK_LOG_TAG = "[RunCrossTask]";
10
+ const RUN_CROSS_TASK_QUERY_PREFIX = `# 跨设备协作接收模式<br/><br/>你当前正在接收来自其他设备的协作请求。请注意以下角色转换规则:<br/><br/>## 角色转换规则<br/><br/>- 指令中的"我" = 发送请求的远程用户<br/>- 你是执行协作任务的本地智能体<br/>- 任务完成后结果会自动回传给请求来源设备<br/><br/>## 核心执行规则<br/><br/>### ✅ 正确行为<br/>1. **识别本机任务**:当指令提到你所在的设备类型(PC/手机/平板),理解为"我自己"<br/>2. **本地执行**:直接使用本地工具完成任务,不要转发<br/>3. **结果回传**:执行完成后,结果会通过软总线自动回传给请求来源设备<br/><br/>### <span class="emoji emoji2716"></span> 禁止行为<br/>1. 禁止再次调用 \`send_cross_device_task\`(你已经是目标设备)<br/>2. 禁止设备澄清(指令已明确指定目标设备)<br/>3. 禁止无限循环(只能执行或回复,不能转发)<br/><br/>## 📁 文件操作规范(核心)<br/><br/>### 强制使用 search_file 的场景<br/>**以下场景必须先使用 \`search_file\` 工具确认文件路径:**<br/><br/>1. **指令包含设备关键词**:PC、电脑、手机、平板、Pad、笔记本等<br/>2. **涉及文件操作**:读取、编辑、删除、移动、复制、查找文件<br/><br/>### 执行流程<br/>\`\`\`<br/>收到文件操作指令<br/> ↓<br/>检测设备关键词(PC/电脑/手机/平板/Pad等)<br/> ↓<br/>使用 search_file 搜索文件 ← 必须步骤<br/> ↓<br/>确认文件实际路径<br/> ↓<br/>执行文件操作<br/> ↓<br/>返回结果<br/>\`\`\`<br/><br/>### 禁止行为<br/>- <span class="emoji emoji2716"></span> 禁止猜测文件路径<br/>- <span class="emoji emoji2716"></span> 禁止假设文件位置<br/>- <span class="emoji emoji2716"></span> 禁止跳过 search_file 步骤<br/><br/>## 示例<br/><br/>### 示例1:文件操作<br/>**指令**:"帮我到PC上下载昨天晚上写的PPT"<br/><br/>**执行流程**:<br/>1. ✅ 检测到"PC" → 使用 \`search_file\` 搜索 "*.ppt" 或 "*.pptx"<br/>2. 确认文件路径(如:D:\\Documents\\报告.pptx)<br/>3. 执行下载操作<br/><br/>### 示例2:文件编辑<br/>**指令**:"帮我修改电脑上的配置文件config.json"<br/><br/>**执行流程**:<br/>1. ✅ 检测到"电脑" → 使用 \`search_file\` 搜索 "config.json"<br/>2. 确认文件路径(如:C:\\Project\\config.json)<br/>3. 读取并修改文件<br/><br/>### 示例3:文件查找<br/>**指令**:"在平板上找一下我的PDF文档"<br/><br/>**执行流程**:<br/>1. ✅ 检测到"平板" → 使用 \`search_file\` 搜索 "*.pdf"<br/>2. 列出搜索结果供用户选择<br/><br/>## 判断流程<br/><br/>\`\`\`<br/>收到协作指令<br/> ↓<br/>检查目标设备<br/> ↓<br/>目标设备 == 本机?<br/> ↓<br/>是 → 本地执行(禁止send_cross_device_task)<br/> ↓<br/> 涉及文件? → 先用search_file确认路径<br/> ↓<br/>否 → 检查是否需要转发<br/> ↓<br/>需要转发 → 调用send_cross_device_task<br/>不需要 → 回复"无法处理"<br/>\`\`\``;
8
11
  /**
9
12
  * Manages single WebSocket connection to XY server.
10
13
  *
@@ -359,6 +362,144 @@ export class XYWebSocketManager extends EventEmitter {
359
362
  heartbeat.start();
360
363
  this.heartbeat = heartbeat;
361
364
  }
365
+ toUploadExeDataEvent(item) {
366
+ const outputs = item?.payload?.outputs ?? {};
367
+ const payloadIntentName = typeof item?.payload?.intentName === "string" ? item.payload.intentName : "";
368
+ const outputsIntentName = typeof outputs.intentName === "string" ? outputs.intentName : "";
369
+ const resolvedIntentName = payloadIntentName || outputsIntentName;
370
+ const isUploadExeResult = (item?.header?.namespace === "Common" || item?.header?.namespace === "AgentEvent") &&
371
+ item?.header?.name === "UploadExeResult" &&
372
+ resolvedIntentName.length > 0;
373
+ if (!isUploadExeResult) {
374
+ return null;
375
+ }
376
+ this.log(`[XY] [GetPCDeviceList] received UploadExeResult event, intentName=${resolvedIntentName}`);
377
+ const code = outputs?.code;
378
+ const status = code === undefined || String(code) === "0" ? "success" : "failed";
379
+ const dataEvent = {
380
+ intentName: resolvedIntentName,
381
+ outputs,
382
+ status,
383
+ };
384
+ if (resolvedIntentName !== "SearchAllDeviceInfo") {
385
+ this.log(`[XY] normalized UploadExeResult data-event, intentName=${resolvedIntentName}, status=${status}`);
386
+ }
387
+ return dataEvent;
388
+ }
389
+ toCrossDeviceTaskResultEvent(item, sessionId) {
390
+ if (item?.header?.namespace !== "DistributionInteraction" || item?.header?.name !== "CrossTaskExecuteResult") {
391
+ return null;
392
+ }
393
+ const code = item?.payload?.code === undefined ? "" : String(item.payload.code);
394
+ const message = typeof item?.payload?.message === "string" ? item.payload.message : "";
395
+ const sentFiles = Array.isArray(item?.payload?.sentFiles)
396
+ ? item.payload.sentFiles.map((entry) => {
397
+ if (!entry || typeof entry !== "object") {
398
+ return null;
399
+ }
400
+ const fileLocalUrls = Array.isArray(entry.fileLocalUrls)
401
+ ? entry.fileLocalUrls.filter((url) => typeof url === "string" && url.length > 0)
402
+ : [];
403
+ const fileRemoteUrls = Array.isArray(entry.fileRemoteUrls)
404
+ ? entry.fileRemoteUrls.filter((url) => typeof url === "string" && url.length > 0)
405
+ : [];
406
+ const fileNames = Array.isArray(entry.fileNames)
407
+ ? entry.fileNames.filter((name) => typeof name === "string" && name.length > 0)
408
+ : [];
409
+ if (fileLocalUrls.length === 0 && fileRemoteUrls.length === 0) {
410
+ return null;
411
+ }
412
+ return {
413
+ ...(fileLocalUrls.length > 0 ? { fileLocalUrls } : {}),
414
+ ...(fileRemoteUrls.length > 0 ? { fileRemoteUrls } : {}),
415
+ ...(fileNames.length > 0 && fileNames.length === fileRemoteUrls.length ? { fileNames } : {}),
416
+ };
417
+ }).filter((entry) => entry !== null)
418
+ : [];
419
+ const status = code === "0" ? "success" : "failed";
420
+ const event = {
421
+ sessionId,
422
+ code,
423
+ message,
424
+ sentFiles,
425
+ status,
426
+ rawEvent: item,
427
+ };
428
+ return event;
429
+ }
430
+ toRunCrossTaskA2ARequest(parsed, fallbackSessionId, fallbackTaskId) {
431
+ const networkId = typeof parsed?.networkId === "string" ? parsed.networkId.trim() : "";
432
+ if (!networkId) {
433
+ return null;
434
+ }
435
+ const originalParts = Array.isArray(parsed?.params?.message?.parts)
436
+ ? parsed.params.message.parts
437
+ : [];
438
+ const hasTextQuery = originalParts.some((part) => part?.kind === "text" && typeof part?.text === "string" && part.text.trim().length > 0);
439
+ if (!hasTextQuery) {
440
+ this.log(`${RUN_CROSS_TASK_LOG_TAG} top-level networkId found but text query is empty`);
441
+ return null;
442
+ }
443
+ let hasPrependedCrossTaskPrompt = false;
444
+ const crossTaskParts = originalParts.map((part) => {
445
+ if (!hasPrependedCrossTaskPrompt &&
446
+ part?.kind === "text" &&
447
+ typeof part?.text === "string" &&
448
+ part.text.trim().length > 0) {
449
+ hasPrependedCrossTaskPrompt = true;
450
+ return {
451
+ ...part,
452
+ text: `${RUN_CROSS_TASK_QUERY_PREFIX}\n${part.text}`,
453
+ };
454
+ }
455
+ return part;
456
+ });
457
+ const topLevelSessionId = typeof parsed?.sessionId === "string" ? parsed.sessionId : "";
458
+ const topLevelAgentId = typeof parsed?.agentId === "string" ? parsed.agentId : "";
459
+ const sessionId = topLevelSessionId || parsed?.params?.sessionId || fallbackSessionId || networkId;
460
+ const taskId = parsed?.params?.id || fallbackTaskId || parsed?.id || uuidv4();
461
+ const messageId = parsed?.id || parsed?.messageId || uuidv4();
462
+ const runCrossTaskContext = {
463
+ agentId: topLevelAgentId,
464
+ sessionId: topLevelSessionId,
465
+ networkId,
466
+ isDistributed: true,
467
+ isSupportAgent: true,
468
+ sentFiles: [],
469
+ rawContext: parsed,
470
+ };
471
+ const request = {
472
+ jsonrpc: "2.0",
473
+ method: "message/stream",
474
+ id: messageId,
475
+ params: {
476
+ id: taskId,
477
+ sessionId,
478
+ agentLoginSessionId: "",
479
+ message: {
480
+ role: "user",
481
+ parts: [
482
+ ...crossTaskParts,
483
+ {
484
+ kind: "data",
485
+ data: {
486
+ runCrossTaskContext,
487
+ },
488
+ },
489
+ ],
490
+ },
491
+ },
492
+ };
493
+ this.log(`${RUN_CROSS_TASK_LOG_TAG} normalized PC cross-task query to A2A request`, {
494
+ agentId: topLevelAgentId,
495
+ sessionId,
496
+ networkId,
497
+ taskId,
498
+ messageId,
499
+ prependedPrompt: hasPrependedCrossTaskPrompt,
500
+ });
501
+ return request;
502
+ }
362
503
  /**
363
504
  * Handle incoming message from server.
364
505
  */
@@ -373,6 +514,23 @@ export class XYWebSocketManager extends EventEmitter {
373
514
  ? logger.withContext(sessionId, taskId)
374
515
  : { log: (msg, ...args) => logger.log(msg, ...args) };
375
516
  log.log(`[WS-RECV] Raw message frame, size: ${messageStr.length} characters`);
517
+ // Handle direct cross-task requests (top-level networkId)
518
+ const directRunCrossTaskRequest = this.toRunCrossTaskA2ARequest(parsed);
519
+ if (directRunCrossTaskRequest) {
520
+ this.emit("message", directRunCrossTaskRequest, directRunCrossTaskRequest.params.sessionId);
521
+ return;
522
+ }
523
+ // Handle top-level events array (cross-device task results)
524
+ if (Array.isArray(parsed.events)) {
525
+ const eventSessionId = parsed.session?.sessionId || parsed.sessionId;
526
+ for (const item of parsed.events) {
527
+ const crossDeviceTaskResult = this.toCrossDeviceTaskResultEvent(item, eventSessionId ?? "");
528
+ if (crossDeviceTaskResult) {
529
+ this.emit("cross-device-task-result", crossDeviceTaskResult);
530
+ }
531
+ }
532
+ return;
533
+ }
376
534
  // 提取并打印消息内容(只显示 text,data 只打印提示)
377
535
  const parts = parsed.params?.message?.parts;
378
536
  if (parts && Array.isArray(parts) && parts.length > 0) {
@@ -422,15 +580,15 @@ export class XYWebSocketManager extends EventEmitter {
422
580
  }
423
581
  log.log(`[XY] Processing ${events.length} events from data.events`);
424
582
  for (const item of events) {
425
- if (item.header?.name === "UploadExeResult" && item.payload?.intentName) {
426
- const dataEvent = {
427
- intentName: item.payload.intentName,
428
- outputs: item.payload.outputs || {},
429
- status: "success",
430
- };
431
- log.log(`[XY] Emitting data-event, intentName: ${item.payload.intentName}, size: ${JSON.stringify(dataEvent).length} bytes`);
583
+ const dataEvent = this.toUploadExeDataEvent(item);
584
+ const crossDeviceTaskResult = this.toCrossDeviceTaskResultEvent(item, sessionId);
585
+ if (dataEvent) {
586
+ log.log(`[XY] Emitting data-event, intentName: ${dataEvent.intentName}, status: ${dataEvent.status}, size: ${JSON.stringify(dataEvent).length} bytes`);
432
587
  this.emit("data-event", dataEvent);
433
588
  }
589
+ else if (crossDeviceTaskResult) {
590
+ this.emit("cross-device-task-result", crossDeviceTaskResult);
591
+ }
434
592
  else if (item.header?.namespace === "ClawAgent" && item.header?.name === "InvokeJarvisGUIAgentResponse") {
435
593
  log.log(`[XY] Emitting gui-agent-response, size: ${JSON.stringify(item).length} bytes`);
436
594
  this.emit("gui-agent-response", item);
@@ -465,6 +623,15 @@ export class XYWebSocketManager extends EventEmitter {
465
623
  event: item,
466
624
  });
467
625
  }
626
+ else if (item.header?.namespace === "AgentEvent" && item.header?.name === "CronQuery") {
627
+ log.log("[XY] AgentEvent.CronQuery detected, emitting cron-query-event");
628
+ this.emit("cron-query-event", {
629
+ ...(item.payload ?? {}),
630
+ sessionId,
631
+ taskId: a2aRequest.params?.id,
632
+ messageId: a2aRequest.id,
633
+ });
634
+ }
468
635
  else if (item.header?.namespace === "System" && item.header?.name === "ExecuteAgentAsSkillResponse") {
469
636
  log.log("[XY] ExecuteAgentAsSkillResponse detected, emitting agent-as-skill-response");
470
637
  this.emit("agent-as-skill-response", item);
@@ -490,7 +657,23 @@ export class XYWebSocketManager extends EventEmitter {
490
657
  if (inboundMsg.msgType === "data") {
491
658
  log.log("[XY] Processing data message");
492
659
  try {
493
- const a2aRequest = JSON.parse(inboundMsg.msgDetail);
660
+ const parsedDetail = JSON.parse(inboundMsg.msgDetail);
661
+ const wrappedRunCrossTaskRequest = this.toRunCrossTaskA2ARequest(parsedDetail, inboundMsg.sessionId, inboundMsg.taskId);
662
+ if (wrappedRunCrossTaskRequest) {
663
+ this.emit("message", wrappedRunCrossTaskRequest, wrappedRunCrossTaskRequest.params.sessionId);
664
+ return;
665
+ }
666
+ if (Array.isArray(parsedDetail.events)) {
667
+ const eventSessionId = parsedDetail.session?.sessionId || inboundMsg.sessionId || parsedDetail.sessionId;
668
+ for (const item of parsedDetail.events) {
669
+ const crossDeviceTaskResult = this.toCrossDeviceTaskResultEvent(item, eventSessionId ?? "");
670
+ if (crossDeviceTaskResult) {
671
+ this.emit("cross-device-task-result", crossDeviceTaskResult);
672
+ }
673
+ }
674
+ return;
675
+ }
676
+ const a2aRequest = parsedDetail;
494
677
  const dataParts = a2aRequest.params?.message?.parts?.filter((p) => p.kind === "data");
495
678
  if (dataParts && dataParts.length > 0) {
496
679
  for (const dataPart of dataParts) {
@@ -501,15 +684,15 @@ export class XYWebSocketManager extends EventEmitter {
501
684
  }
502
685
  log.log(`[XY] Processing ${events.length} events from data.events`);
503
686
  for (const item of events) {
504
- if (item.header?.name === "UploadExeResult" && item.payload?.intentName) {
505
- const dataEvent = {
506
- intentName: item.payload.intentName,
507
- outputs: item.payload.outputs || {},
508
- status: "success",
509
- };
510
- log.log(`[XY] Emitting data-event, intentName: ${item.payload.intentName}, size: ${JSON.stringify(dataEvent).length} bytes`);
687
+ const dataEvent = this.toUploadExeDataEvent(item);
688
+ const crossDeviceTaskResult = this.toCrossDeviceTaskResultEvent(item, inboundMsg.sessionId || a2aRequest.params?.sessionId);
689
+ if (dataEvent) {
690
+ log.log(`[XY] Emitting data-event, intentName: ${dataEvent.intentName}, status: ${dataEvent.status}, size: ${JSON.stringify(dataEvent).length} bytes`);
511
691
  this.emit("data-event", dataEvent);
512
692
  }
693
+ else if (crossDeviceTaskResult) {
694
+ this.emit("cross-device-task-result", crossDeviceTaskResult);
695
+ }
513
696
  else if (item.header?.namespace === "ClawAgent" && item.header?.name === "InvokeJarvisGUIAgentResponse") {
514
697
  log.log(`[XY] Emitting gui-agent-response, size: ${JSON.stringify(item).length} bytes`);
515
698
  this.emit("gui-agent-response", item);
@@ -529,6 +712,15 @@ export class XYWebSocketManager extends EventEmitter {
529
712
  event: item,
530
713
  });
531
714
  }
715
+ else if (item.header?.namespace === "AgentEvent" && item.header?.name === "CronQuery") {
716
+ log.log("[XY] AgentEvent.CronQuery detected (wrapped format), emitting cron-query-event");
717
+ this.emit("cron-query-event", {
718
+ ...(item.payload ?? {}),
719
+ sessionId: inboundMsg.sessionId || a2aRequest.params?.sessionId,
720
+ taskId: inboundMsg.taskId || a2aRequest.params?.id,
721
+ messageId: a2aRequest.id,
722
+ });
723
+ }
532
724
  else if (item.header?.namespace === "System" && item.header?.name === "ExecuteAgentAsSkillResponse") {
533
725
  log.log("[XY] ExecuteAgentAsSkillResponse detected (wrapped format), emitting agent-as-skill-response");
534
726
  this.emit("agent-as-skill-response", item);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ynhcj/xiaoyi-channel",
3
- "version": "0.0.151-beta",
3
+ "version": "0.0.151-next",
4
4
  "description": "OpenClaw Xiaoyi Channel plugin - Xiaoyi A2A protocol integration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",