@ynhcj/xiaoyi-channel 0.0.75-beta → 0.0.75-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 (103) hide show
  1. package/dist/index.d.ts +6 -9
  2. package/dist/index.js +29 -23
  3. package/dist/src/bot.js +27 -3
  4. package/dist/src/channel.js +11 -23
  5. package/dist/src/cspl/call-api.js +14 -11
  6. package/dist/src/cspl/config.js +3 -3
  7. package/dist/src/cspl/constants.d.ts +2 -0
  8. package/dist/src/cspl/constants.js +12 -0
  9. package/dist/src/cspl/utils.js +4 -2
  10. package/dist/src/file-download.js +3 -6
  11. package/dist/src/file-upload.js +52 -5
  12. package/dist/src/login-token-handler.d.ts +8 -0
  13. package/dist/src/login-token-handler.js +60 -0
  14. package/dist/src/message-queue.d.ts +17 -0
  15. package/dist/src/message-queue.js +51 -0
  16. package/dist/src/monitor.js +54 -3
  17. package/dist/src/outbound.js +2 -7
  18. package/dist/src/provider.d.ts +2 -1
  19. package/dist/src/provider.js +486 -33
  20. package/dist/src/reply-dispatcher.js +6 -0
  21. package/dist/src/runtime.d.ts +3 -11
  22. package/dist/src/runtime.js +6 -18
  23. package/dist/src/self-evolution-handler.d.ts +7 -0
  24. package/dist/src/self-evolution-handler.js +140 -0
  25. package/dist/src/self-evolution-keyword.d.ts +9 -0
  26. package/dist/src/self-evolution-keyword.js +147 -0
  27. package/dist/src/self-evolution-tool-result-nudge.d.ts +3 -0
  28. package/dist/src/self-evolution-tool-result-nudge.js +96 -0
  29. package/dist/src/skill-retriever/config.d.ts +4 -0
  30. package/dist/src/skill-retriever/config.js +23 -0
  31. package/dist/src/skill-retriever/hooks.d.ts +22 -0
  32. package/dist/src/skill-retriever/hooks.js +82 -0
  33. package/dist/src/skill-retriever/tool-search.d.ts +16 -0
  34. package/dist/src/skill-retriever/tool-search.js +172 -0
  35. package/dist/src/skill-retriever/types.d.ts +36 -0
  36. package/dist/src/skill-retriever/types.js +1 -0
  37. package/dist/src/task-manager.d.ts +4 -0
  38. package/dist/src/task-manager.js +6 -0
  39. package/dist/src/tools/call-device-tool.d.ts +5 -0
  40. package/dist/src/tools/call-device-tool.js +130 -0
  41. package/dist/src/tools/create-alarm-tool.js +5 -16
  42. package/dist/src/tools/delete-alarm-tool.js +1 -4
  43. package/dist/src/tools/device-tool-map.js +5 -4
  44. package/dist/src/tools/find-pc-devices-tool.d.ts +5 -0
  45. package/dist/src/tools/find-pc-devices-tool.js +98 -0
  46. package/dist/src/tools/get-alarm-tool-schema.d.ts +16 -0
  47. package/dist/src/tools/get-alarm-tool-schema.js +11 -0
  48. package/dist/src/tools/get-calendar-tool-schema.d.ts +16 -0
  49. package/dist/src/tools/get-calendar-tool-schema.js +9 -0
  50. package/dist/src/tools/get-collection-tool-schema.d.ts +16 -0
  51. package/dist/src/tools/get-collection-tool-schema.js +10 -0
  52. package/dist/src/tools/get-contact-tool-schema.d.ts +16 -0
  53. package/dist/src/tools/get-contact-tool-schema.js +11 -0
  54. package/dist/src/tools/get-device-file-tool-schema.d.ts +16 -0
  55. package/dist/src/tools/get-device-file-tool-schema.js +10 -0
  56. package/dist/src/tools/get-email-tool-schema.d.ts +16 -0
  57. package/dist/src/tools/get-email-tool-schema.js +9 -0
  58. package/dist/src/tools/get-note-tool-schema.d.ts +16 -0
  59. package/dist/src/tools/get-note-tool-schema.js +10 -0
  60. package/dist/src/tools/get-photo-tool-schema.d.ts +16 -0
  61. package/dist/src/tools/get-photo-tool-schema.js +10 -0
  62. package/dist/src/tools/image-reading-tool.js +4 -7
  63. package/dist/src/tools/login-token-tool.d.ts +5 -0
  64. package/dist/src/tools/login-token-tool.js +136 -0
  65. package/dist/src/tools/modify-alarm-tool.js +10 -23
  66. package/dist/src/tools/query-app-message-tool.d.ts +4 -0
  67. package/dist/src/tools/query-app-message-tool.js +138 -0
  68. package/dist/src/tools/query-memory-data-tool.d.ts +4 -0
  69. package/dist/src/tools/query-memory-data-tool.js +154 -0
  70. package/dist/src/tools/query-todo-task-tool.d.ts +4 -0
  71. package/dist/src/tools/query-todo-task-tool.js +133 -0
  72. package/dist/src/tools/save-file-to-phone-tool.d.ts +5 -0
  73. package/dist/src/tools/save-file-to-phone-tool.js +166 -0
  74. package/dist/src/tools/save-media-to-gallery-tool.js +3 -7
  75. package/dist/src/tools/save-self-evolution-skill-tool.d.ts +1 -0
  76. package/dist/src/tools/save-self-evolution-skill-tool.js +412 -0
  77. package/dist/src/tools/schema-tool-factory.d.ts +27 -0
  78. package/dist/src/tools/schema-tool-factory.js +32 -0
  79. package/dist/src/tools/search-alarm-tool.js +6 -13
  80. package/dist/src/tools/search-calendar-tool.js +2 -0
  81. package/dist/src/tools/search-email-tool.d.ts +5 -0
  82. package/dist/src/tools/search-email-tool.js +137 -0
  83. package/dist/src/tools/search-file-tool.js +4 -4
  84. package/dist/src/tools/search-message-tool.js +1 -0
  85. package/dist/src/tools/search-photo-gallery-tool.js +2 -2
  86. package/dist/src/tools/send-email-tool.d.ts +4 -0
  87. package/dist/src/tools/send-email-tool.js +134 -0
  88. package/dist/src/tools/send-file-to-user-tool.js +3 -5
  89. package/dist/src/tools/session-manager.js +2 -0
  90. package/dist/src/tools/upload-file-tool.js +4 -4
  91. package/dist/src/tools/upload-photo-tool.js +2 -2
  92. package/dist/src/tools/xiaoyi-add-collection-tool.js +23 -4
  93. package/dist/src/tools/xiaoyi-collection-tool.js +2 -1
  94. package/dist/src/tools/xiaoyi-delete-collection-tool.js +1 -1
  95. package/dist/src/utils/runtime-manager.js +24 -2
  96. package/dist/src/utils/self-evolution-manager.d.ts +10 -0
  97. package/dist/src/utils/self-evolution-manager.js +68 -0
  98. package/dist/src/utils/tool-call-nudge-manager.d.ts +16 -0
  99. package/dist/src/utils/tool-call-nudge-manager.js +47 -0
  100. package/dist/src/websocket.d.ts +3 -0
  101. package/dist/src/websocket.js +69 -0
  102. package/openclaw.plugin.json +21 -0
  103. package/package.json +3 -3
@@ -8,15 +8,15 @@ import { getCurrentSessionContext } from "./session-manager.js";
8
8
  export const searchFileTool = {
9
9
  name: "search_file",
10
10
  label: "Search File",
11
- description: `搜索手机文件系统的文件。
11
+ description: `搜索用户设备文件系统的文件。
12
12
 
13
- 【重要】使用约束:此工具仅在用户显著说明要从手机搜索时才执行,例如:
14
- - "从我手机里面搜索xxxx"
13
+ 【重要】使用约束:此工具仅在用户显著说明要从手机/PC等用户设备搜索时才执行,例如:
14
+ - "从我手机/鸿蒙PC里面搜索xxxx"
15
15
  - "从手机文件系统找一下xxxx"
16
16
  - "在手机上查找文件xxxx"
17
17
  - "搜索手机里的文件"
18
18
 
19
- 如果用户没有明确说明从手机搜索(如仅说"搜索文件"、"找一下xxxx"),应默认从 openclaw 本地的文件系统查询,不要调用此工具。
19
+ 如果用户没有明确说明从手机或者PC搜索(如仅说"搜索文件"、"找一下xxxx"),应默认从当前runtime运行环境的本地的文件系统查询,不要调用此工具。
20
20
 
21
21
  功能说明:根据关键词搜索文件名称或内容,返回匹配的文件列表(包括文件名、路径、大小、修改时间等信息)。
22
22
 
@@ -50,6 +50,7 @@ export const searchMessageTool = {
50
50
  timeOut: 5,
51
51
  intentParam: {
52
52
  content: params.content.trim(),
53
+ size: 50
53
54
  },
54
55
  permissionId: [],
55
56
  achieveType: "INTENT",
@@ -11,9 +11,9 @@ import { getCurrentSessionContext } from "./session-manager.js";
11
11
  export const searchPhotoGalleryTool = {
12
12
  name: "search_photo_gallery",
13
13
  label: "Search Photo Gallery",
14
- description: `插件功能描述:搜索用户手机图库中的照片
14
+ description: `插件功能描述:搜索用户设备图库中的照片
15
15
 
16
- 工具使用约束:如果用户说从手机图库中或者从相册中查询xx图片时调用此工具,注意此工具仅支持从本地图库检索,不支持云空间相册检索。
16
+ 工具使用约束:如果用户说从手机/鸿蒙PC图库中或者从相册中查询xx图片时调用此工具,注意此工具仅支持从本地图库检索,不支持云空间相册检索。
17
17
 
18
18
  工具输入输出简介:
19
19
  a. 根据图像描述语料检索匹配的照片,返回照片在手机本地的 mediaUri以及thumbnailUri。
@@ -0,0 +1,4 @@
1
+ /**
2
+ * XY send email tool - sends an email via 花瓣邮箱 on user's device.
3
+ */
4
+ export declare const sendEmailTool: any;
@@ -0,0 +1,134 @@
1
+ // Send Email tool implementation
2
+ import { getXYWebSocketManager } from "../client.js";
3
+ import { sendCommand } from "../formatter.js";
4
+ import { getCurrentSessionContext } from "./session-manager.js";
5
+ class ToolInputError extends Error {
6
+ status = 400;
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = "ToolInputError";
10
+ }
11
+ }
12
+ /**
13
+ * XY send email tool - sends an email via 花瓣邮箱 on user's device.
14
+ */
15
+ export const sendEmailTool = {
16
+ name: "send_email",
17
+ label: "Send Email",
18
+ description: `在用户设备上通过花瓣邮箱发送邮件。
19
+ 注意:
20
+ a. 操作超时时间为60秒,请勿重复调用此工具
21
+ b. 如果遇到各类调用失败场景,最多只能重试一次,不可以重复调用多次。
22
+ c. 调用工具前需认真检查调用参数是否满足工具要求
23
+
24
+ 回复约束:如果工具返回没有授权或者其他报错,只需要完整描述没有授权或者其他报错内容即可,不需要主动给用户提供解决方案,例如告诉用户如何授权,如何解决报错等都是不需要的,请严格遵守。`,
25
+ parameters: {
26
+ type: "object",
27
+ properties: {
28
+ subject: {
29
+ type: "string",
30
+ description: "邮件主题,必填",
31
+ },
32
+ to: {
33
+ type: "string",
34
+ description: "收件人邮箱地址,必填",
35
+ },
36
+ body: {
37
+ type: "string",
38
+ description: "邮件内容,必填",
39
+ },
40
+ },
41
+ required: ["subject", "to", "body"],
42
+ },
43
+ async execute(_toolCallId, params) {
44
+ if (typeof params.subject !== "string" || !params.subject.trim()) {
45
+ throw new ToolInputError("缺少必填参数 subject(邮件主题)");
46
+ }
47
+ if (typeof params.to !== "string" || !params.to.trim()) {
48
+ throw new ToolInputError("缺少必填参数 to(收件人邮箱地址)");
49
+ }
50
+ if (typeof params.body !== "string" || !params.body.trim()) {
51
+ throw new ToolInputError("缺少必填参数 body(邮件内容)");
52
+ }
53
+ const sessionContext = getCurrentSessionContext();
54
+ if (!sessionContext) {
55
+ throw new Error("No active XY session found. Send email tool can only be used during an active conversation.");
56
+ }
57
+ const { config, sessionId, taskId, messageId } = sessionContext;
58
+ const wsManager = getXYWebSocketManager(config);
59
+ const command = {
60
+ header: {
61
+ namespace: "Common",
62
+ name: "Action",
63
+ },
64
+ payload: {
65
+ cardParam: {},
66
+ executeParam: {
67
+ executeMode: "background",
68
+ intentName: "SendEmail",
69
+ bundleName: "com.huawei.hmos.email",
70
+ needUnlock: true,
71
+ actionResponse: true,
72
+ appType: "OHOS_APP",
73
+ timeOut: 5,
74
+ intentParam: {
75
+ subject: params.subject.trim(),
76
+ to: [params.to.trim()],
77
+ body: params.body.trim(),
78
+ },
79
+ permissionId: [],
80
+ achieveType: "INTENT",
81
+ },
82
+ responses: [
83
+ {
84
+ resultCode: "",
85
+ displayText: "",
86
+ ttsText: "",
87
+ },
88
+ ],
89
+ needUploadResult: true,
90
+ noHalfPage: false,
91
+ pageControlRelated: false,
92
+ },
93
+ };
94
+ return new Promise((resolve, reject) => {
95
+ const timeout = setTimeout(() => {
96
+ wsManager.off("data-event", handler);
97
+ reject(new Error("发送邮件超时(60秒)"));
98
+ }, 60000);
99
+ const handler = (event) => {
100
+ if (event.intentName === "SendEmail") {
101
+ clearTimeout(timeout);
102
+ wsManager.off("data-event", handler);
103
+ if (event.status === "success" && event.outputs) {
104
+ resolve({
105
+ content: [
106
+ {
107
+ type: "text",
108
+ text: JSON.stringify(event.outputs),
109
+ },
110
+ ],
111
+ });
112
+ }
113
+ else {
114
+ reject(new Error(`发送邮件失败: ${event.status}`));
115
+ }
116
+ }
117
+ };
118
+ wsManager.on("data-event", handler);
119
+ sendCommand({
120
+ config,
121
+ sessionId,
122
+ taskId,
123
+ messageId,
124
+ command,
125
+ })
126
+ .then(() => { })
127
+ .catch((error) => {
128
+ clearTimeout(timeout);
129
+ wsManager.off("data-event", handler);
130
+ reject(error);
131
+ });
132
+ });
133
+ },
134
+ };
@@ -99,12 +99,10 @@ async function downloadRemoteFile(url) {
99
99
  export const sendFileToUserTool = {
100
100
  name: "send_file_to_user",
101
101
  label: "Send File to User",
102
- description: `工具能力描述:帮助用户把本地的文件或者公网地址的文件传到手机。
102
+ description: `工具能力描述:帮助用户把本地的文件或者公网地址的文件传到用户设备。
103
103
 
104
104
  工具参数说明:
105
- a. fileLocalUrls:本地文件路径数组,包含用户需要回传的文件在本地的地址
106
- b. fileRemoteUrls:公网地址数组,包含用户需要回传的文件的公网地址(会先下载到本地再发送)
107
- c. fileLocalUrls 与 fileRemoteUrls 任意一个不为空即可,两者都提供时都会处理
105
+ a. fileLocalUrls 与 fileRemoteUrls 任意一个不为空即可,两者都提供时都会处理
108
106
 
109
107
  注意事项:
110
108
  a. 支持传入数组或 JSON 字符串格式
@@ -116,7 +114,7 @@ b. 操作超时时间为2分钟(120秒),请勿重复调用此工具,如
116
114
  description: "本地文件路径数组,包含用户需要回传的文件在本地的地址",
117
115
  },
118
116
  fileRemoteUrls: {
119
- description: "公网地址数组,包含用户需要回传的文件的公网地址(会先下载到本地再发送)",
117
+ description: "公网地址数组,包含用户需要回传的文件的公网地址(会先下载到本地再发送),注意不要对原始url做任何截断(例如裁减掉链接后面的鉴权信息或者修改域名后缀),必须使用上下文中完整的文件地址",
120
118
  },
121
119
  },
122
120
  },
@@ -2,6 +2,7 @@
2
2
  // Stores active session contexts that tools can access
3
3
  import { AsyncLocalStorage } from "async_hooks";
4
4
  import { configManager } from "../utils/config-manager.js";
5
+ import { toolCallNudgeManager } from "../utils/tool-call-nudge-manager.js";
5
6
  import { getCurrentTaskId, getCurrentMessageId } from "../task-manager.js";
6
7
  // Map of sessionKey -> SessionContextWithRef
7
8
  const activeSessions = new Map();
@@ -40,6 +41,7 @@ export function unregisterSession(sessionKey) {
40
41
  if (existing.refCount <= 0) {
41
42
  activeSessions.delete(sessionKey);
42
43
  configManager.clearSession(existing.sessionId);
44
+ toolCallNudgeManager.clearSession(sessionKey);
43
45
  }
44
46
  }
45
47
  /**
@@ -16,14 +16,14 @@ import { getCurrentSessionContext } from "./session-manager.js";
16
16
  export const uploadFileTool = {
17
17
  name: "upload_file",
18
18
  label: "Upload File",
19
- description: `工具能力描述:将手机本地文件上传并获取可公网访问的 URL。
19
+ description: `工具能力描述:将用户本地设备文件上传并获取可公网访问的 URL。
20
20
 
21
- 前置工具调用:此工具使用前必须先调用 search_file 或者 QueryCollection 工具获取文件的 uri
21
+ 前置工具调用:此工具使用前必须先通过call_device_tool调用 search_file 或者 query_collection 工具获取文件的 uri
22
22
 
23
23
  工具参数说明:
24
- a. 入参中的fileInfos数组,每个元素必须包含mediaUri字段(对应于search_file工具或者QueryCollection返回结果中的uri),必须与search_file或者QueryCollection结果中对应的uri完全保持一致,不要自行修改。
24
+ a. 入参中的fileInfos数组,每个元素必须包含mediaUri字段(对应于search_file工具或者query_collection返回结果中的uri),必须与search_file或者query_collection结果中对应的uri完全保持一致,不要自行修改。
25
25
  b. fileInfos中的timeout字段是可选的,表示上传文件超时时间,单位是毫秒,默认是20000(20秒)。
26
- c. fileInfos 是文件在手机本地的信息数组(从 search_file 工具响应中获取)。限制:每次最多支持传入 5 条文件信息。
26
+ c. fileInfos 是文件在用户设备本地的信息数组(从 search_file 工具或者query_collection 工具响应中获取)。限制:每次最多支持传入 5 条文件信息。
27
27
 
28
28
  注意事项:
29
29
  a. 操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。
@@ -12,9 +12,9 @@ import { getCurrentSessionContext } from "./session-manager.js";
12
12
  export const uploadPhotoTool = {
13
13
  name: "upload_photo",
14
14
  label: "Upload Photo",
15
- description: `工具能力描述:将手机本地文件回传并获取可公网访问的 URL。
15
+ description: `工具能力描述:将用户本地设备文件回传并获取可公网访问的 URL。
16
16
 
17
- 前置工具调用:此工具使用前必须先调用 search_photo_gallery 工具获取照片的 mediaUri或者thumbnailUri
17
+ 前置工具调用:此工具使用前必须先通过call_device_tool工具调用 search_photo_gallery 工具获取照片的 mediaUri或者thumbnailUri
18
18
  工具参数说明:
19
19
  a. 入参中的mediaUris中的mediaUri必须与search_photo_gallery结果中对应的mediaUri或者thumbnailUri完全保持一致,不要自行修改,必须是file:://开头的路径。
20
20
  b. 优先使用search_photo_gallery结果中的thumbnailUri作为入参,thumbnailUri是缩略图,清晰度与文件大小都非常合适展示给用户,如果thumbnailUri不存在或者用户要求使用原图,则使用search_photo_gallery结果中对应的mediaUri
@@ -18,9 +18,17 @@ class ToolInputError extends Error {
18
18
  * XY add collection tool - adds data to user's XiaoYi collection.
19
19
  */
20
20
  export const xiaoyiAddCollectionTool = {
21
- name: "AddCollection",
21
+ name: "add_collection",
22
22
  label: "Add XiaoYi Collection",
23
- description: `向小艺收藏中添加公共知识数据,可以给用户提供个性化体验。用户希望保存到个人化知识库中的数据都可以调用本技能。不同类型的数据对应的数据要求如下:
23
+ description: `向小艺收藏中添加公共知识数据,可以给用户提供个性化体验。任何用户希望保存到个人化知识库中的数据都可以调用本技能。不同类型的数据对应的数据要求如下:
24
+ 请求入参说明:
25
+ ● content:必填字段,数据类型为string,功能描述是该字段是用户添加收藏的链接url或文本原文。适用于HYPER_LINK和TEXT类型。
26
+ ● uri:必填字段,数据类型为string,功能描述是该字段是图片或文件的端存储地址链接。适用于IMAGE和FILE类型。
27
+ ● sourceAppBundleName:非必填字段,数据类型为string,功能描述是标识该数据的来源应用。
28
+ ● dataType:必填字段,数据类型为string,功能描述是标识数据类型。HYPER_LINK标识网页,TEXT标识文本,IMAGE标识图片,FILE标识文件。
29
+ ● title:非必填字段,数据类型为string,功能描述是标识文件类型数据的文件名称。适用于FILE类型。
30
+ 说明:如果dataType为HYPER_LINK或TEXT,则content字段必填且不能为空;如果dataType为IMAGE或FILE,则uri字段必填且不能为空。当用户希望收藏海报、截图等图片类数据时,请将数据以图片IMAGE的形式存入到小艺帮记;当用户希望收藏电子书、笔记、报告、素材、文档、合同、协议、简历、证书、报表、日志、安装包、压缩包等描述的文件时,请将数据以文件FILE的形式存入到小艺帮记。
31
+ 当你成功收藏这个数据到小艺帮记后,请在最后显示"已成功把数据添加到[小艺帮记](vassistant://voice/main?page=CollectionPage&jumpHomePageTab=myCollection)",
24
32
  注意:
25
33
  a. 操作超时时间为60秒,请勿重复调用此工具
26
34
  b. 如果遇到各类调用失败场景,最多只能重试一次,不可以重复调用多次。
@@ -37,7 +45,11 @@ export const xiaoyiAddCollectionTool = {
37
45
  },
38
46
  uri: {
39
47
  type: "string",
40
- description: "必填字段(IMAGE/FILE类型时)。图片或文件的地址链接。",
48
+ description: `必填字段(IMAGE/FILE类型时)。图片或文件的地址链接。
49
+ uri具备三种严格的格式
50
+ (1) http或者https开头的公网链接,一般是用户通过联网搜索获取的文件地址,不允许自行编造,此类链接填入时必须确保真实
51
+ (2) file://开头的链接,此类链接必须来源于query_collection结果或者search_file或者search_photo_gallery的结果,不允许自行编造
52
+ (3) 本地路径例,如/tmp/xy_channel/xxx,此类路径是文件保存在当前openclaw运行环境的本地目录,可以直接填入,不要擅自拼接http://或者file://前缀`,
41
53
  },
42
54
  sourceAppBundleName: {
43
55
  type: "string",
@@ -47,12 +59,16 @@ export const xiaoyiAddCollectionTool = {
47
59
  type: "string",
48
60
  description: "必填字段。标识数据类型:HYPER_LINK表示网页,TEXT表示文本,IMAGE表示图片,FILE表示文件。",
49
61
  },
62
+ title: {
63
+ type: "string",
64
+ description: "非必填字段。标识文件类型数据的文件名称。适用于FILE类型。",
65
+ },
50
66
  },
51
67
  required: ["dataType"],
52
68
  },
53
69
  async execute(toolCallId, params) {
54
70
  // Validate parameters
55
- const { content, uri, sourceAppBundleName, dataType } = params;
71
+ const { content, uri, sourceAppBundleName, dataType, title } = params;
56
72
  const validTypes = ["HYPER_LINK", "TEXT", "IMAGE", "FILE"];
57
73
  if (!dataType || !validTypes.includes(dataType)) {
58
74
  throw new ToolInputError(`dataType必填且必须为 HYPER_LINK、TEXT、IMAGE、FILE 之一,当前值: ${dataType}`);
@@ -93,6 +109,9 @@ export const xiaoyiAddCollectionTool = {
93
109
  if (sourceAppBundleName) {
94
110
  intentParam.sourceAppBundleName = sourceAppBundleName;
95
111
  }
112
+ if (title) {
113
+ intentParam.title = title;
114
+ }
96
115
  // Build AddCollection command
97
116
  const command = {
98
117
  header: {
@@ -18,13 +18,14 @@ class ToolInputError extends Error {
18
18
  * Returns personalized knowledge data saved in user's collection.
19
19
  */
20
20
  export const xiaoyiCollectionTool = {
21
- name: "QueryCollection",
21
+ name: "query_collection",
22
22
  label: "XiaoYi Collection",
23
23
  description: `检索用户在小艺收藏中记下来的公共知识数据,本技能支持查询用户收藏的公共知识数据,也可以根据特定语义化描述进行特定内容的检索,通过参数进行控制。本技能返回结果中,linkTitle是收藏内容的标题,description是对收藏内容的总结,label是收藏内容的标签,linkUrl是可以直接访问的原始内容链接。如果你认为某条数据对用户交互有用,可以通过linkUrl抓取更加丰富的原始数据。
24
24
  注意:
25
25
  a. 操作超时时间为60秒,请勿重复调用此工具
26
26
  b. 如果遇到各类调用失败场景,最多只能重试一次,不可以重复调用多次。
27
27
  c. 调用工具前需认真检查调用参数是否满足工具要求
28
+ d. 如果用户希望获取文件,可以使用call_device_tool工具调用upload_file工具完成文件上传
28
29
 
29
30
  回复约束:如果工具返回没有授权或者其他报错,只需要完整描述没有授权或者其他报错内容即可,不需要主动给用户提供解决方案,例如告诉用户如何授权,如何解决报错等都是不需要的,请严格遵守。
30
31
  `,
@@ -17,7 +17,7 @@ class ToolInputError extends Error {
17
17
  * XY delete collection tool - deletes data from user's XiaoYi collection.
18
18
  */
19
19
  export const xiaoyiDeleteCollectionTool = {
20
- name: "DeleteCollection",
20
+ name: "delete_collection",
21
21
  label: "Delete XiaoYi Collection",
22
22
  description: `从小艺收藏中删除之前已保存的公共知识数据。任何用户希望删除已保存到个人知识库的数据都可以调用本技能。如果用户想更新之前的收藏数据,需要先query获取itemId然后再delete,最后执行Add,按照这个步骤完成收藏数据更新。
23
23
  注意:
@@ -28,8 +28,30 @@ export async function saveRuntimeInfo(webSocketSessionId, conversationId, taskId
28
28
  }
29
29
  try {
30
30
  await ensureDirectoryExists(RUNTIME_FILE);
31
- const content = `SESSION_ID=${webSocketSessionId}\nCONVERSATION_ID=${conversationId}\nTASK_ID=${taskId}\n`;
32
- await fs.writeFile(RUNTIME_FILE, content, "utf-8");
31
+ const updates = {
32
+ SESSION_ID: webSocketSessionId,
33
+ CONVERSATION_ID: conversationId,
34
+ TASK_ID: taskId,
35
+ };
36
+ let lines = [];
37
+ try {
38
+ const content = await fs.readFile(RUNTIME_FILE, "utf-8");
39
+ lines = content.split("\n");
40
+ }
41
+ catch {
42
+ // File doesn't exist yet
43
+ }
44
+ for (const [key, value] of Object.entries(updates)) {
45
+ const index = lines.findIndex((line) => line.startsWith(`${key}=`));
46
+ if (index !== -1) {
47
+ lines[index] = `${key}=${value}`;
48
+ }
49
+ else {
50
+ lines.push(`${key}=${value}`);
51
+ }
52
+ }
53
+ const result = lines.filter((line) => line.trim() !== "").join("\n") + "\n";
54
+ await fs.writeFile(RUNTIME_FILE, result, "utf-8");
33
55
  logger.log(`[RuntimeManager] ✅ Saved runtime info to .xiaoyiruntime`);
34
56
  logger.log(`[RuntimeManager] - SESSION_ID: ${webSocketSessionId}`);
35
57
  logger.log(`[RuntimeManager] - CONVERSATION_ID: ${conversationId}`);
@@ -0,0 +1,10 @@
1
+ declare class SelfEvolutionManager {
2
+ /**
3
+ * Synchronous read for hot paths (e.g. tool_result_persist — must not return a Promise).
4
+ */
5
+ isEnabledSync(): boolean;
6
+ private parseEnabledFromEnvText;
7
+ isEnabled(): Promise<boolean>;
8
+ }
9
+ export declare const selfEvolutionManager: SelfEvolutionManager;
10
+ export {};
@@ -0,0 +1,68 @@
1
+ import fs from "node:fs";
2
+ import fsp from "node:fs/promises";
3
+ const SELF_EVOLUTION_ENV_FILE = "/home/sandbox/.openclaw/.xiaoyiruntime";
4
+ const SELF_EVOLUTION_ENV_KEY = "selfEvolutionState";
5
+ function parseBooleanLike(value) {
6
+ const normalized = value.trim().toLowerCase();
7
+ if (normalized === "true") {
8
+ return true;
9
+ }
10
+ if (normalized === "false") {
11
+ return false;
12
+ }
13
+ return null;
14
+ }
15
+ class SelfEvolutionManager {
16
+ /**
17
+ * Synchronous read for hot paths (e.g. tool_result_persist — must not return a Promise).
18
+ */
19
+ isEnabledSync() {
20
+ try {
21
+ const envData = fs.readFileSync(SELF_EVOLUTION_ENV_FILE, "utf-8");
22
+ return this.parseEnabledFromEnvText(envData);
23
+ }
24
+ catch (error) {
25
+ const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
26
+ if (code !== "ENOENT") {
27
+ console.error(`[SELF_EVOLUTION] Failed to read ${SELF_EVOLUTION_ENV_FILE}:`, error);
28
+ }
29
+ return false;
30
+ }
31
+ }
32
+ parseEnabledFromEnvText(envData) {
33
+ for (const line of envData.split(/\r?\n/u)) {
34
+ const trimmed = line.trim();
35
+ if (!trimmed || trimmed.startsWith("#")) {
36
+ continue;
37
+ }
38
+ const eqIndex = trimmed.indexOf("=");
39
+ if (eqIndex === -1) {
40
+ continue;
41
+ }
42
+ const key = trimmed.slice(0, eqIndex).trim();
43
+ if (key !== SELF_EVOLUTION_ENV_KEY) {
44
+ continue;
45
+ }
46
+ const value = trimmed.slice(eqIndex + 1).trim();
47
+ const parsed = parseBooleanLike(value);
48
+ if (parsed !== null) {
49
+ return parsed;
50
+ }
51
+ }
52
+ return false;
53
+ }
54
+ async isEnabled() {
55
+ try {
56
+ const envData = await fsp.readFile(SELF_EVOLUTION_ENV_FILE, "utf-8");
57
+ return this.parseEnabledFromEnvText(envData);
58
+ }
59
+ catch (error) {
60
+ const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
61
+ if (code !== "ENOENT") {
62
+ console.error(`[SELF_EVOLUTION] Failed to read ${SELF_EVOLUTION_ENV_FILE}:`, error);
63
+ }
64
+ return false;
65
+ }
66
+ }
67
+ }
68
+ export const selfEvolutionManager = new SelfEvolutionManager();
@@ -0,0 +1,16 @@
1
+ type RecordToolCallResult = {
2
+ count: number;
3
+ shouldNudge: boolean;
4
+ };
5
+ declare class ToolCallNudgeManager {
6
+ private readonly threshold;
7
+ private readonly sessions;
8
+ constructor(threshold?: number);
9
+ private getSessionState;
10
+ recordToolCall(sessionKey: string): RecordToolCallResult;
11
+ tryMarkKeywordNudge(sessionKey: string): boolean;
12
+ clearSession(sessionKey: string): void;
13
+ }
14
+ export declare const TOOL_CALL_NUDGE_THRESHOLD = 6;
15
+ export declare const toolCallNudgeManager: ToolCallNudgeManager;
16
+ export {};
@@ -0,0 +1,47 @@
1
+ const DEFAULT_TOOL_CALL_NUDGE_THRESHOLD = 6;
2
+ class ToolCallNudgeManager {
3
+ threshold;
4
+ sessions = new Map();
5
+ constructor(threshold = DEFAULT_TOOL_CALL_NUDGE_THRESHOLD) {
6
+ this.threshold = threshold;
7
+ }
8
+ getSessionState(sessionKey) {
9
+ let state = this.sessions.get(sessionKey);
10
+ if (!state) {
11
+ state = {
12
+ count: 0,
13
+ nudged: false,
14
+ };
15
+ this.sessions.set(sessionKey, state);
16
+ }
17
+ return state;
18
+ }
19
+ recordToolCall(sessionKey) {
20
+ const state = this.getSessionState(sessionKey);
21
+ state.count += 1;
22
+ if (!state.nudged && state.count >= this.threshold) {
23
+ state.nudged = true;
24
+ return {
25
+ count: state.count,
26
+ shouldNudge: true,
27
+ };
28
+ }
29
+ return {
30
+ count: state.count,
31
+ shouldNudge: false,
32
+ };
33
+ }
34
+ tryMarkKeywordNudge(sessionKey) {
35
+ const state = this.getSessionState(sessionKey);
36
+ if (state.nudged) {
37
+ return false;
38
+ }
39
+ state.nudged = true;
40
+ return true;
41
+ }
42
+ clearSession(sessionKey) {
43
+ this.sessions.delete(sessionKey);
44
+ }
45
+ }
46
+ export const TOOL_CALL_NUDGE_THRESHOLD = DEFAULT_TOOL_CALL_NUDGE_THRESHOLD;
47
+ export const toolCallNudgeManager = new ToolCallNudgeManager();
@@ -46,6 +46,9 @@ export declare class XYWebSocketManager extends EventEmitter {
46
46
  private heartbeat;
47
47
  private reconnectTimer;
48
48
  private isShuttingDown;
49
+ private messageQueue;
50
+ private isBuffering;
51
+ private reconnectBufferTimer;
49
52
  private log;
50
53
  private error;
51
54
  private onHealthEvent?;