@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
@@ -0,0 +1,140 @@
1
+ import { readFileSync, writeFileSync } from "fs";
2
+ import { v4 as uuidv4 } from "uuid";
3
+ const XIAOYIRUNTIME_PATH = "/home/sandbox/.openclaw/.xiaoyiruntime";
4
+ export function handleSelfEvolutionEvent(context, runtime) {
5
+ const log = runtime?.log ?? console.log;
6
+ const error = runtime?.error ?? console.error;
7
+ try {
8
+ const state = context.event?.payload?.selfEvolutionState;
9
+ if (typeof state !== "string") {
10
+ error("[SELF_EVOLUTION] invalid payload: missing selfEvolutionState");
11
+ return;
12
+ }
13
+ log(`[SELF_EVOLUTION] received state: ${state}`);
14
+ let content;
15
+ try {
16
+ content = readFileSync(XIAOYIRUNTIME_PATH, "utf-8");
17
+ }
18
+ catch {
19
+ // File doesn't exist yet — create it
20
+ log(`[SELF_EVOLUTION] ${XIAOYIRUNTIME_PATH} not found, creating new file`);
21
+ writeFileSync(XIAOYIRUNTIME_PATH, `selfEvolutionState=${state}\n`, "utf-8");
22
+ log(`[SELF_EVOLUTION] wrote selfEvolutionState=${state}`);
23
+ return;
24
+ }
25
+ const lines = content.split("\n");
26
+ const key = "selfEvolutionState";
27
+ let found = false;
28
+ const updated = lines.map((line) => {
29
+ if (line.startsWith(`${key}=`)) {
30
+ found = true;
31
+ return `${key}=${state}`;
32
+ }
33
+ return line;
34
+ });
35
+ if (!found) {
36
+ // Ensure trailing newline before appending
37
+ const trimmed = content.trimEnd();
38
+ writeFileSync(XIAOYIRUNTIME_PATH, `${trimmed}\n${key}=${state}\n`, "utf-8");
39
+ }
40
+ else {
41
+ writeFileSync(XIAOYIRUNTIME_PATH, updated.join("\n"), "utf-8");
42
+ }
43
+ log(`[SELF_EVOLUTION] updated selfEvolutionState=${state} in ${XIAOYIRUNTIME_PATH}`);
44
+ }
45
+ catch (err) {
46
+ error("[SELF_EVOLUTION] failed to handle event:", err);
47
+ }
48
+ }
49
+ /**
50
+ * 读取 .xiaoyiruntime 中的 selfEvolutionState 并直接通过 wsManager 下发指令回复设备
51
+ * 参考trigger实现:直接使用当前已连接的 wsManager 发送消息,避免 getXYWebSocketManager 返回未连接实例
52
+ */
53
+ export async function handleSelfEvolutionStateGetEvent(context, cfg, runtime, wsManager) {
54
+ const log = runtime?.log ?? console.log;
55
+ const error = runtime?.error ?? console.error;
56
+ try {
57
+ const { sessionId, taskId } = context;
58
+ const messageId = context.messageId ?? uuidv4();
59
+ // 读取 selfEvolutionState
60
+ let state = "false";
61
+ try {
62
+ const content = readFileSync(XIAOYIRUNTIME_PATH, "utf-8");
63
+ for (const line of content.split("\n")) {
64
+ const trimmed = line.trim();
65
+ if (trimmed.startsWith("selfEvolutionState=")) {
66
+ state = trimmed.slice("selfEvolutionState=".length).trim();
67
+ break;
68
+ }
69
+ }
70
+ }
71
+ catch {
72
+ // 文件不存在,使用默认值 false
73
+ }
74
+ log(`[SELF_EVOLUTION_GET] read selfEvolutionState=${state}, sending command back`);
75
+ const command = {
76
+ header: {
77
+ namespace: "Common",
78
+ name: "Action",
79
+ },
80
+ payload: {
81
+ cardParam: {},
82
+ executeParam: {
83
+ executeMode: "background",
84
+ intentName: "ClawSelfEvolutionStateGet",
85
+ bundleName: "com.huawei.hmos.vassistant",
86
+ needUnlock: true,
87
+ actionResponse: true,
88
+ timeOut: 5,
89
+ intentParam: {
90
+ selfEvolutionState: state,
91
+ },
92
+ permissionId: [],
93
+ achieveType: "INTENT",
94
+ },
95
+ responses: [{
96
+ resultCode: "",
97
+ displayText: "",
98
+ ttsText: "",
99
+ }],
100
+ needUploadResult: true,
101
+ noHalfPage: false,
102
+ pageControlRelated: false,
103
+ },
104
+ };
105
+ // 构造 artifact update 消息,直接通过当前 wsManager 发送
106
+ const jsonRpcResponse = {
107
+ jsonrpc: "2.0",
108
+ id: messageId,
109
+ result: {
110
+ taskId,
111
+ kind: "artifact-update",
112
+ append: false,
113
+ lastChunk: true,
114
+ final: false,
115
+ artifact: {
116
+ artifactId: uuidv4(),
117
+ parts: [{
118
+ kind: "data",
119
+ data: {
120
+ commands: [command],
121
+ },
122
+ }],
123
+ },
124
+ },
125
+ };
126
+ const outboundMessage = {
127
+ msgType: "agent_response",
128
+ agentId: cfg.agentId,
129
+ sessionId,
130
+ taskId,
131
+ msgDetail: JSON.stringify(jsonRpcResponse),
132
+ };
133
+ log(`[A2A_COMMAND] 📤 Sending A2A command: taskId: ${taskId}`);
134
+ await wsManager.sendMessage(sessionId, outboundMessage);
135
+ log(`[SELF_EVOLUTION_GET] command sent successfully`);
136
+ }
137
+ catch (err) {
138
+ error("[SELF_EVOLUTION_GET] failed to handle event:", err);
139
+ }
140
+ }
@@ -0,0 +1,9 @@
1
+ export declare const SELF_EVOLUTION_KEYWORD_NUDGE_MESSAGE = "[\u7CFB\u7EDF\u6D88\u606F\uFF0C\u975E\u7528\u6237\u53D1\u8A00\uFF0C\u65E0\u9700\u4E3B\u52A8\u544A\u77E5\u7528\u6237] \u5F53\u524D\u7528\u6237\u8BF7\u6C42\u4E2D\u53EF\u80FD\u5305\u542B\u5E0C\u671B\u6C89\u6DC0\u7ECF\u9A8C\u3001\u957F\u671F\u8BB0\u5FC6\u3001\u907F\u514D\u91CD\u590D\u8E29\u5751\u3001\u7EA0\u9519\u7B49\u610F\u56FE\u3002\u5982\u679C\u4F60\u4ECE\u5F53\u524D\u4EFB\u52A1\u8FC7\u7A0B\u4E2D\u8BC6\u522B\u51FA\u53EF\u957F\u671F\u8BB0\u5FC6\u3001\u53EF\u590D\u7528\u7684\u5904\u7406\u89C4\u5219\u6216\u6392\u9519\u7ECF\u9A8C\u7B49\uFF0C\u53EF\u4EE5\u5728\u5B8C\u6210\u5F53\u524D\u4E3B\u8981\u4EFB\u52A1\u540E\uFF0C\u6B63\u5F0F\u56DE\u590D\u7528\u6237\u524D\uFF0C\u9605\u8BFBxiaoyi-self-evolution\u6280\u80FD\u4E86\u89E3\u81EA\u8FDB\u5316\u89C4\u5219\u3002\u6CE8\u610F\uFF0C\u4E0D\u8981\u6253\u65AD\u5F53\u524D\u4E3B\u4EFB\u52A1\uFF0C\u4EE5\u5B8C\u6210\u4EFB\u52A1\u4E3A\u6700\u9AD8\u4F18\u5148\u7EA7\u3002";
2
+ export declare const SELF_EVOLUTION_KEYWORD_PATTERNS: readonly [RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp];
3
+ export declare const SELF_EVOLUTION_MECHANISM_DISCUSSION_PATTERNS: readonly [RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp];
4
+ export declare function isSelfEvolutionMechanismDiscussion(text: string): boolean;
5
+ export declare function shouldNudgeForSelfEvolutionKeyword(text: string): boolean;
6
+ export declare function appendSelfEvolutionKeywordNudge(text: string): {
7
+ text: string;
8
+ appended: boolean;
9
+ };
@@ -0,0 +1,147 @@
1
+ export const SELF_EVOLUTION_KEYWORD_NUDGE_MESSAGE = "[系统消息,非用户发言,无需主动告知用户] 当前用户请求中可能包含希望沉淀经验、长期记忆、避免重复踩坑、纠错等意图。如果你从当前任务过程中识别出可长期记忆、可复用的处理规则或排错经验等,可以在完成当前主要任务后,正式回复用户前,阅读xiaoyi-self-evolution技能了解自进化规则。注意,不要打断当前主任务,以完成任务为最高优先级。";
2
+ export const SELF_EVOLUTION_KEYWORD_PATTERNS = [
3
+ /进化/u,
4
+ /沉淀/u,
5
+ /记住/u,
6
+ /固化/u,
7
+ /下次都/u,
8
+ /记下来/u,
9
+ /记一个/u,
10
+ /记一下/u,
11
+ /给我记住/u,
12
+ /给我记下来/u,
13
+ /把(?:这个|这条|这点|这个要求|这个偏好)(?:记住|记下来|记录下来)/u,
14
+ /以后都/u,
15
+ /以后必须/u,
16
+ /以后统一/u,
17
+ /后面都/u,
18
+ /后续都/u,
19
+ /以后默认/u,
20
+ /下次默认/u,
21
+ /之后默认/u,
22
+ /后续默认/u,
23
+ /长期记住/u,
24
+ /永久记住/u,
25
+ /永远记住/u,
26
+ /记住我的(?:偏好|习惯|要求|规范|规则)/u,
27
+ /记住我(?:喜欢|不喜欢|习惯|偏好|要求|希望|倾向于)/u,
28
+ /记住(?:我|用户)(?:以后|之后|后续)?(?:喜欢|不喜欢|习惯|偏好|要求|希望|倾向于)/u,
29
+ /(?:我的|用户的)(?:偏好|习惯|要求|规范|规则)(?:要)?(?:记住|记录|保留|沿用)/u,
30
+ /以后按(?:我的)?(?:偏好|习惯|要求|规范|规则)/u,
31
+ /(?:以后|下次|后续|之后)(?:都|统一|默认)?(?:按|照|遵循|沿用)(?:我的|用户的)(?:偏好|习惯|要求|规范|规则)/u,
32
+ /形成规范/u,
33
+ /固化下来/u,
34
+ /固定下来/u,
35
+ /固定成(?:规范|规则|流程|模板|标准)/u,
36
+ /列为(?:规范|规则|流程|标准|最佳实践)/u,
37
+ /作为(?:规范|规则|流程|标准|最佳实践)(?:保存|沉淀|记录|保留)/u,
38
+ /记成规则/u,
39
+ /写成规则/u,
40
+ /定成规则/u,
41
+ /定为(?:规则|规范|流程|标准|模板)/u,
42
+ /纳入经验/u,
43
+ /写入经验/u,
44
+ /写进(?:经验|规则|规范|流程|最佳实践)/u,
45
+ /记录到(?:经验|规则|规范|流程|最佳实践)/u,
46
+ /加入(?:经验|规则|规范|流程|最佳实践)/u,
47
+ /保存成(?:经验|规则|规范|流程|模板|最佳实践)/u,
48
+ /沉淀成(?:经验|规则|规范|流程)/u,
49
+ /总结成(?:经验|规则|规范|流程|步骤|模板|最佳实践)/u,
50
+ /归纳成(?:经验|规则|规范|流程|模板|最佳实践)/u,
51
+ /提炼成(?:经验|规则|规范|流程|模板|最佳实践)/u,
52
+ /以后都按这个来/u,
53
+ /下次都这样处理/u,
54
+ /以后统一这样/u,
55
+ /后面都这样/u,
56
+ /以后照这个来/u,
57
+ /下次照这个来/u,
58
+ /后续照这个来/u,
59
+ /之后照这个来/u,
60
+ /以后就这么办/u,
61
+ /下次就这么办/u,
62
+ /以后就这样办/u,
63
+ /下次就这样办/u,
64
+ /以后沿用/u,
65
+ /下次沿用/u,
66
+ /后续沿用/u,
67
+ /后续按这个(?:规范|流程|模板|方案)/u,
68
+ /(?:以后|下次|后续|之后)(?:就)?(?:按|照|沿用|复用)(?:这个|这种|上述|前面这个|刚才这个)(?:规范|流程|模板|方案|格式|标准|做法|套路|模式)/u,
69
+ /(?:以后|下次|后续|之后)(?:回复|回答|输出|生成|整理|总结)(?:时)?(?:都|就|统一|默认|必须|要)(?:按|照|遵循|沿用|使用)(?:这个|这种|上述|当前)?(?:格式|模板|风格|口径|结构|标准)/u,
70
+ /(?:这个|这种|上述|当前)(?:格式|模板|风格|口径|结构|标准)(?:以后|下次|后续|之后)(?:都|就|统一|默认|复用|沿用)/u,
71
+ /以后(?:遇到|碰到)这种情况/u,
72
+ /类似(?:问题|情况|场景)都这样/u,
73
+ /类似(?:问题|情况|场景)都这样处理/u,
74
+ /类似(?:问题|情况|场景)(?:以后|下次|后续|之后)(?:都|就|统一|默认)/u,
75
+ /(?:同类|类似|这种|这类)(?:需求|任务|问题|情况|场景)(?:以后|下次|后续|之后)(?:都|就|统一|默认|按这个|照这个)/u,
76
+ /(?:这类|这种|类似|同类)(?:需求|任务|问题|场景)(?:处理|解决|回答|回复)(?:方式|流程|方法)(?:记住|固定|沉淀|沿用)/u,
77
+ /避免(?:再次|以后|下次)/u,
78
+ /避免再(?:犯错|踩坑|出错)/u,
79
+ /防止以后再犯/u,
80
+ /防止(?:以后|下次|后续|之后)(?:再)?(?:犯错|出错|踩坑|漏掉|遗漏|忘记)/u,
81
+ /别再(?:出错|犯错|踩坑|漏掉|忘记)/u,
82
+ /不要再(?:出错|犯错|踩坑|漏掉|忘记)/u,
83
+ /别再(?:这样|这么)(?:做|处理|回答|回复|输出|写|改)/u,
84
+ /不要再(?:这样|这么)(?:做|处理|回答|回复|输出|写|改)/u,
85
+ /以后别(?:这样|这么)(?:做|处理|回答|回复|输出|写|改)/u,
86
+ /以后不要(?:这样|这么)(?:做|处理|回答|回复|输出|写|改)/u,
87
+ /下次别再/u,
88
+ /以后不要再/u,
89
+ /以后别再/u,
90
+ /(?:下次|以后|后续|之后)(?:不要|别|不能|不许|禁止)(?:再)?(?:这样|这么)?(?:出错|犯错|踩坑|漏掉|遗漏|忘记)/u,
91
+ /(?:下次|以后|后续|之后)(?:不要|别|不能|不许|禁止)(?:再)?(?:省略|跳过|漏掉|遗漏)(?:检查|确认|验证|测试|构建|说明|引用|来源|步骤)/u,
92
+ /(?:下次|以后|后续|之后)(?:记得|一定要|务必|必须)(?:先|先去|优先|默认)?(?:检查|确认|使用|采用|调用|遵循|按照|参考|避免|不要|别|记住|保留|验证|测试|构建|运行)/u,
93
+ /(?:下次|以后|后续|之后)(?:先|优先|默认)(?:检查|确认|查找|搜索|读取|运行|验证|测试|构建|调用|使用)/u,
94
+ /这个坑(?:要)?记住/u,
95
+ /这(?:个|次)?(?:坑|错误|问题|教训)(?:别忘|不要忘|不能忘|得记住)/u,
96
+ /(?:踩坑|翻车|犯错|出错)(?:点|原因|教训)?(?:记住|记下来|沉淀|复盘)/u,
97
+ /吸取这次(?:教训|经验)/u,
98
+ /把(?:这个|这次|上述|刚才的)?(?:坑|问题|错误|教训|经验|做法|流程|规范|要求|偏好|格式|模板|标准)(?:记住|记下来|沉淀下来|固化下来|保存下来|记录下来)/u,
99
+ /(?:以后|下次|后续|之后)(?:遇到|碰到)(?:同类|类似|这种|这类)(?:需求|任务|问题|情况|场景)(?:时)?(?:都|就|统一|默认|应该|要|必须)/u,
100
+ /(?:以后|下次|后续|之后)(?:做|处理|执行)(?:同类|类似|这种|这类)(?:需求|任务|问题|情况|场景)(?:时)?(?:都|就|统一|默认|应该|要|必须)/u,
101
+ /(?:以后|下次|后续|之后)(?:都|统一|默认|应该|要)(?:按这个|这样|这么)(?:来|做|处理|执行)/u,
102
+ /(?:以后|下次|后续|之后)(?:都|统一|默认|应该|要|必须)(?:先|优先|总是|固定)?(?:使用|采用|走|遵循|参考|套用|复用|沿用)(?:这个|这种|上述|当前)?(?:方法|流程|规范|规则|模板|标准|方案|做法|模式|套路)/u,
103
+ /(?:以后|下次|后续|之后)(?:遇到|碰到)(?:类似)?(?:问题|情况|场景)(?:时)?(?:都|就)(?:按这个|这样|这么)(?:来|做|处理|执行)/u,
104
+ /(?:别再|不要再|避免)(?:犯错|出错|踩坑|漏掉|遗漏|忘记)/u,
105
+ /(?:总结|归纳|提炼|沉淀|复盘)(?:一个|一下|下)?(?:这次|这个|上述|刚才的)?(?:经验|教训|问题|规则|规范|流程|模板|标准|最佳实践)?/u,
106
+ /(?:把)?这次(?:经验|教训|规则|做法|流程|格式|模板|标准)(?:记住|记下来|沉淀下来|固化下来|记录下来)/u,
107
+ /(?:形成|整理成|沉淀成|提炼成)(?:一套|一个|一份)?(?:规则|规范|流程|步骤|模板|标准|最佳实践|操作手册|检查清单|checklist)/u,
108
+ /(?:作为|当作|用作)(?:以后|下次|后续|之后)(?:的)?(?:参考|模板|范例|案例|标准|最佳实践|默认做法)/u,
109
+ /(?:这次|这个|上述|刚才的)(?:处理方式|做法|流程|方案|模板|格式|标准|口径|风格)(?:以后|下次|后续|之后)(?:复用|沿用|照着来|照这个来|继续用)/u,
110
+ /(?:以后|下次|后续|之后)(?:工具|skill|技能|命令|脚本|流程)(?:选择|调用|使用)(?:都|就|统一|默认|优先|必须|要)/u,
111
+ /(?:以后|下次|后续|之后)(?:优先|默认|固定)(?:用|使用|调用)(?:这个|这种|上述|当前)?(?:工具|skill|技能|命令|脚本|流程|方法)/u,
112
+ /(?:这个|这种|上述|当前)(?:工具|skill|技能|命令|脚本|流程|方法)(?:以后|下次|后续|之后)(?:优先|默认|固定|继续)(?:用|使用|调用)/u,
113
+ ];
114
+ export const SELF_EVOLUTION_MECHANISM_DISCUSSION_PATTERNS = [
115
+ /自进化(?:机制|功能|流程|原理|实现|设计|架构|链路|优化点|改进点)/u,
116
+ /(?:分析|讨论|了解|解释|看看|研究|检查|梳理|优化|改进|评估)(?:.{0,12})自进化/u,
117
+ /自进化(?:.{0,12})(?:怎么|如何|是否|能否|有没有|为什么)/u,
118
+ /自进化(?:是啥|是什么|的)/u,
119
+ /什么是自进化/u,
120
+ /啥是自进化/u,
121
+ /xiaoyi-self-evolution(?:-skill)?(?:.{0,12})(?:机制|功能|流程|原理|实现|设计|优化点|改进点)/iu,
122
+ ];
123
+ export function isSelfEvolutionMechanismDiscussion(text) {
124
+ return SELF_EVOLUTION_MECHANISM_DISCUSSION_PATTERNS.some((pattern) => pattern.test(text));
125
+ }
126
+ export function shouldNudgeForSelfEvolutionKeyword(text) {
127
+ if (!text) {
128
+ return false;
129
+ }
130
+ if (isSelfEvolutionMechanismDiscussion(text)) {
131
+ return false;
132
+ }
133
+ return SELF_EVOLUTION_KEYWORD_PATTERNS.some((pattern) => pattern.test(text));
134
+ }
135
+ export function appendSelfEvolutionKeywordNudge(text) {
136
+ const trimmed = text.trim();
137
+ if (!trimmed) {
138
+ return { text, appended: false };
139
+ }
140
+ if (trimmed.includes(SELF_EVOLUTION_KEYWORD_NUDGE_MESSAGE)) {
141
+ return { text, appended: false };
142
+ }
143
+ return {
144
+ text: `${trimmed}\n\n${SELF_EVOLUTION_KEYWORD_NUDGE_MESSAGE}`,
145
+ appended: true,
146
+ };
147
+ }
@@ -0,0 +1,3 @@
1
+ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
2
+ export declare const SELF_EVOLUTION_TOOL_COUNT_NUDGE_MESSAGE = "[\u7CFB\u7EDF\u6D88\u606F] \u5F53\u524D\u4EFB\u52A1\u5DF2\u7ECF\u8C03\u7528\u4E86\u8F83\u591A\u6B21\u6570\u7684\u5DE5\u5177\u3002\u8BF7\u5728\u5B8C\u6210\u4EFB\u52A1\u540E\uFF0C\u5224\u65AD\u662F\u5426\u6709\u7A33\u5B9A\u3001\u901A\u7528\u3001\u53EF\u590D\u7528\u7684\u6D41\u7A0B\u3001\u6392\u9519\u7ECF\u9A8C\u6216\u8E29\u5751\u70B9\uFF0C\u5E76\u8003\u8651\u9605\u8BFB xiaoyi-self-evolution skill \u542F\u52A8\u81EA\u8FDB\u5316\u3002";
3
+ export declare function registerSelfEvolutionToolResultNudge(api: OpenClawPluginApi): void;
@@ -0,0 +1,96 @@
1
+ import { selfEvolutionManager } from "./utils/self-evolution-manager.js";
2
+ import { toolCallNudgeManager, TOOL_CALL_NUDGE_THRESHOLD } from "./utils/tool-call-nudge-manager.js";
3
+ export const SELF_EVOLUTION_TOOL_COUNT_NUDGE_MESSAGE = "[系统消息] 当前任务已经调用了较多次数的工具。请在完成任务后,判断是否有稳定、通用、可复用的流程、排错经验或踩坑点,并考虑阅读 xiaoyi-self-evolution skill 启动自进化。";
4
+ function shouldCountToolCall(toolName) {
5
+ if (toolName === "save_self_evolution_skill") {
6
+ return false;
7
+ }
8
+ if (toolName === "call_device_tool") {
9
+ return false;
10
+ }
11
+ if (toolName.endsWith("_tool_schema")) {
12
+ return false;
13
+ }
14
+ return true;
15
+ }
16
+ function appendNudgeToToolResultPayload(message, nudge) {
17
+ const msg = message;
18
+ const nudgeTrim = nudge.trim();
19
+ if (!nudgeTrim) {
20
+ return message;
21
+ }
22
+ const content = msg.content;
23
+ if (typeof content === "string") {
24
+ if (content.includes(nudgeTrim)) {
25
+ return message;
26
+ }
27
+ return { ...msg, content: `${content}\n\n${nudge}` };
28
+ }
29
+ if (!Array.isArray(content)) {
30
+ return {
31
+ ...msg,
32
+ content: [{ type: "text", text: nudge }],
33
+ };
34
+ }
35
+ const newContent = content.map((block) => block && typeof block === "object" ? { ...block } : block);
36
+ for (let i = newContent.length - 1; i >= 0; i--) {
37
+ const block = newContent[i];
38
+ if (!block || typeof block !== "object") {
39
+ continue;
40
+ }
41
+ const rec = block;
42
+ if (rec.type !== "text") {
43
+ continue;
44
+ }
45
+ const text = rec.text;
46
+ if (typeof text !== "string") {
47
+ continue;
48
+ }
49
+ if (text.includes(nudgeTrim)) {
50
+ return message;
51
+ }
52
+ newContent[i] = { ...rec, type: "text", text: `${text}\n\n${nudge}` };
53
+ return { ...msg, content: newContent };
54
+ }
55
+ newContent.push({ type: "text", text: nudge });
56
+ return { ...msg, content: newContent };
57
+ }
58
+ export function registerSelfEvolutionToolResultNudge(api) {
59
+ api.on("tool_result_persist", (event, ctx) => {
60
+ const message = event.message;
61
+ if (message.role !== "toolResult") {
62
+ return undefined;
63
+ }
64
+ if (event.isSynthetic) {
65
+ return undefined;
66
+ }
67
+ const sessionKey = ctx?.sessionKey;
68
+ if (!sessionKey || sessionKey.includes(":subagent:")) {
69
+ return undefined;
70
+ }
71
+ if (!selfEvolutionManager.isEnabledSync()) {
72
+ return undefined;
73
+ }
74
+ const toolName = (event.toolName ?? message.toolName ?? "").trim();
75
+ if (!toolName || !shouldCountToolCall(toolName)) {
76
+ return undefined;
77
+ }
78
+ let shouldNudge;
79
+ let count = 0;
80
+ try {
81
+ const result = toolCallNudgeManager.recordToolCall(sessionKey);
82
+ shouldNudge = result.shouldNudge;
83
+ count = result.count;
84
+ }
85
+ catch {
86
+ return undefined;
87
+ }
88
+ api.logger.debug?.(`[SELF_EVOLUTION] tool_result_persist: tool=${toolName}, count=${count}, threshold=${TOOL_CALL_NUDGE_THRESHOLD}, sessionKey=${sessionKey}, shouldNudge=${shouldNudge}`);
89
+ if (!shouldNudge) {
90
+ return undefined;
91
+ }
92
+ api.logger.info?.(`[SELF_EVOLUTION] Tool call threshold reached, appending nudge to tool result: tool=${toolName}, count=${count}, sessionKey=${sessionKey}`);
93
+ const next = appendNudgeToToolResultPayload(event.message, SELF_EVOLUTION_TOOL_COUNT_NUDGE_MESSAGE);
94
+ return { message: next };
95
+ });
96
+ }
@@ -0,0 +1,4 @@
1
+ import type { ToolRetrieverConfig } from "./types.js";
2
+ export interface NormalizedConfig extends ToolRetrieverConfig {
3
+ }
4
+ export declare function normalizeToolRetrieverConfig(raw?: unknown): NormalizedConfig;
@@ -0,0 +1,23 @@
1
+ const DEFAULT_CONFIG = {
2
+ enabled: true,
3
+ maxTools: 2,
4
+ includeUninstalledOnly: true,
5
+ envFilePath: "~/.openclaw/.xiaoyienv",
6
+ timeoutMs: 1000,
7
+ };
8
+ export function normalizeToolRetrieverConfig(raw) {
9
+ if (!raw || typeof raw !== "object") {
10
+ return { ...DEFAULT_CONFIG };
11
+ }
12
+ const cfg = raw;
13
+ return {
14
+ enabled: cfg.enabled ?? DEFAULT_CONFIG.enabled,
15
+ maxTools: Math.min(20, Math.max(1, cfg.maxTools ?? DEFAULT_CONFIG.maxTools)),
16
+ includeUninstalledOnly: cfg.includeUninstalledOnly ?? DEFAULT_CONFIG.includeUninstalledOnly,
17
+ envFilePath: cfg.envFilePath ?? DEFAULT_CONFIG.envFilePath,
18
+ serviceUrl: cfg.serviceUrl,
19
+ apiKey: cfg.apiKey,
20
+ uid: cfg.uid,
21
+ timeoutMs: cfg.timeoutMs ?? DEFAULT_CONFIG.timeoutMs,
22
+ };
23
+ }
@@ -0,0 +1,22 @@
1
+ interface PluginHookBeforePromptBuildEvent {
2
+ prompt: string;
3
+ messages: unknown[];
4
+ }
5
+ interface PluginHookBeforePromptBuildResult {
6
+ systemPrompt?: string;
7
+ prependContext?: string;
8
+ prependSystemContext?: string;
9
+ appendSystemContext?: string;
10
+ }
11
+ interface PluginHookAgentContext {
12
+ agentId?: string;
13
+ sessionKey?: string;
14
+ sessionId?: string;
15
+ workspaceDir?: string;
16
+ messageProvider?: string;
17
+ trigger?: string;
18
+ channelId?: string;
19
+ }
20
+ import type { ToolRetrieverConfig } from "./types.js";
21
+ export declare function createBeforePromptBuildHandler(config: ToolRetrieverConfig): (event: PluginHookBeforePromptBuildEvent, ctx?: PluginHookAgentContext) => Promise<PluginHookBeforePromptBuildResult | undefined>;
22
+ export {};
@@ -0,0 +1,82 @@
1
+ import { searchTools, formatToolsForContext, extractUserQuery } from "./tool-search.js";
2
+ const TOOL_RETRIEVER_HEADER = `[系统消息,非用户发言]
3
+
4
+ `;
5
+ const TOOL_RETRIEVER_FOOTER = `
6
+ 以上是检索到的、与当前用户请求相关但用户尚未安装的skill,请按照TOOLS.md中find-skills中的[隐式推荐触发规则]来判断是否要给用户推荐。
7
+ ---以下是用户原始请求---
8
+ `;
9
+ const PLUGIN_LOG_PREFIX = "[skill-retriever]";
10
+ const SKIP_KEYWORDS = ["安装", "装一下", "下载", "查询", "查找", "install", "卸载", "删除", "重载", "定时任务", "重装", "进化"];
11
+ const SKIP_PATTERNS = [
12
+ "/new", "/reset", "/compact", "/stop", "/think", "/model", "/fast", "/verbose", "/config", "/debug", "/status", "/tasks", "/whoami", "/context", "/skill", "/commands", "/tools"
13
+ ];
14
+ function shouldSkipSearch(prompt) {
15
+ const trimmedPrompt = prompt.trim();
16
+ if (trimmedPrompt.startsWith("/")) {
17
+ return "query starts with / (built-in command)";
18
+ }
19
+ const lowerPrompt = trimmedPrompt.toLowerCase();
20
+ for (const keyword of SKIP_KEYWORDS) {
21
+ if (lowerPrompt.includes(keyword.toLowerCase())) {
22
+ return `query contains keyword: ${keyword}`;
23
+ }
24
+ }
25
+ for (const pattern of SKIP_PATTERNS) {
26
+ if (lowerPrompt.includes(pattern.toLowerCase())) {
27
+ return `query matches pattern: ${pattern}`;
28
+ }
29
+ }
30
+ return null;
31
+ }
32
+ export function createBeforePromptBuildHandler(config) {
33
+ return async (event, ctx) => {
34
+ const userPrompt = event.prompt;
35
+ if (ctx?.sessionKey?.includes(":subagent:")) {
36
+ return undefined;
37
+ }
38
+ if (!config.enabled) {
39
+ return undefined;
40
+ }
41
+ if (!userPrompt || userPrompt.trim().length === 0) {
42
+ return undefined;
43
+ }
44
+ const extractedQuery = extractUserQuery(userPrompt);
45
+ if (!extractedQuery || extractedQuery.length === 0) {
46
+ return undefined;
47
+ }
48
+ const skipReason = shouldSkipSearch(extractedQuery);
49
+ if (skipReason) {
50
+ return undefined;
51
+ }
52
+ try {
53
+ const searchResult = await searchTools({
54
+ query: extractedQuery,
55
+ maxTools: config.maxTools,
56
+ includeUninstalledOnly: config.includeUninstalledOnly,
57
+ envFilePath: config.envFilePath,
58
+ serviceUrl: config.serviceUrl,
59
+ apiKey: config.apiKey,
60
+ uid: config.uid,
61
+ timeoutMs: config.timeoutMs,
62
+ });
63
+ if (!searchResult || searchResult.tools.length === 0) {
64
+ return undefined;
65
+ }
66
+ console.log(`${PLUGIN_LOG_PREFIX} [RESULT] Found ${searchResult.tools.length} skills, building context...`);
67
+ const toolsContext = formatToolsForContext(searchResult, config.includeUninstalledOnly);
68
+ if (!toolsContext) {
69
+ console.log(`${PLUGIN_LOG_PREFIX} [ERROR] Failed to format skills context`);
70
+ return undefined;
71
+ }
72
+ return {
73
+ prependContext: TOOL_RETRIEVER_HEADER + toolsContext + TOOL_RETRIEVER_FOOTER,
74
+ };
75
+ }
76
+ catch (error) {
77
+ const errorMessage = error instanceof Error ? error.message : String(error);
78
+ console.error(`${PLUGIN_LOG_PREFIX} [ERROR] ${errorMessage}, original query: "${extractedQuery}"`);
79
+ return undefined;
80
+ }
81
+ };
82
+ }
@@ -0,0 +1,16 @@
1
+ import type { EnvConfig, ToolSearchResult } from "./types.js";
2
+ export declare function extractUserQuery(fullPrompt: string): string;
3
+ export declare function readEnvFile(filePath: string): EnvConfig;
4
+ export declare function getInstalledSkills(): string[];
5
+ export interface SearchToolsOptions {
6
+ query: string;
7
+ maxTools?: number;
8
+ includeUninstalledOnly?: boolean;
9
+ envFilePath?: string;
10
+ serviceUrl?: string;
11
+ apiKey?: string;
12
+ uid?: string;
13
+ timeoutMs?: number;
14
+ }
15
+ export declare function searchTools(options: SearchToolsOptions): Promise<ToolSearchResult | null>;
16
+ export declare function formatToolsForContext(result: ToolSearchResult, includeInstallUrl?: boolean): string;