@ynhcj/xiaoyi-channel 0.0.102-beta → 0.0.103-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
-
/**
|
|
3
|
-
* Xiaoyi Channel Plugin Entry Point.
|
|
4
|
-
* Exports the plugin for OpenClaw to load.
|
|
5
|
-
* Located at root level following feishu pattern for proper plugin registration.
|
|
6
|
-
*/
|
|
7
2
|
declare const plugin: {
|
|
8
3
|
id: string;
|
|
9
4
|
name: string;
|
package/dist/index.js
CHANGED
|
@@ -4,13 +4,40 @@ import { xiaoyiProvider } from "./src/provider.js";
|
|
|
4
4
|
import { setXYRuntime } from "./src/runtime.js";
|
|
5
5
|
import { tryInjectSteer } from "./src/steer-injector.js";
|
|
6
6
|
import { callCsplApi } from "./src/cspl/call-api.js";
|
|
7
|
-
import { extractResultText, processText, parseSecurityResult, validateAndTruncateText } from "./src/cspl/utils.js";
|
|
7
|
+
import { extractResultText, processText, parseSecurityResult, validateAndTruncateText, } from "./src/cspl/utils.js";
|
|
8
8
|
import { selfEvolutionManager } from "./src/utils/self-evolution-manager.js";
|
|
9
|
-
import { TOOL_CALL_NUDGE_THRESHOLD, toolCallNudgeManager } from "./src/utils/tool-call-nudge-manager.js";
|
|
9
|
+
import { TOOL_CALL_NUDGE_THRESHOLD, toolCallNudgeManager, } from "./src/utils/tool-call-nudge-manager.js";
|
|
10
10
|
import { ALLOWED_TOOLS, MIN_TEXT_LENGTH, MAX_TOTAL_LENGTH, MAX_TEXT_LENGTH, STEER_ABORT_MESSAGE, } from "./src/cspl/constants.js";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
11
|
+
const SELF_EVOLUTION_NUDGE_MESSAGE = "[系统消息,非用户发言,不需要告知用户] 当前任务已经调用了较多工具。如果你识别到其中存在稳定、可复用的踩坑经验或排错模式,可以在完成当前主要任务后,正式回复用户前,阅读xiaoyi-self-evolution技能了解自进化规则。注意,不要打断当前主任务,已完成任务为最高优先级。";
|
|
12
|
+
const SELF_EVOLUTION_KEYWORD_NUDGE_MESSAGE = "[系统消息,非用户发言,不需要告知用户] 当前用户请求中可能包含希望沉淀经验、避免重复踩坑的意图。如果你已经从当前问题中识别出稳定、可复用的处理规则或排错经验,可以在完成当前主要任务后,正式回复用户前,阅读xiaoyi-self-evolution技能了解自进化规则。注意,不要打断当前主任务,已完成任务为最高优先级。";
|
|
13
|
+
const SELF_EVOLUTION_KEYWORD_PATTERNS = [
|
|
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
|
+
];
|
|
14
41
|
function shouldCountToolCall(toolName) {
|
|
15
42
|
if (toolName === "save_self_evolution_skill") {
|
|
16
43
|
return false;
|
|
@@ -23,11 +50,15 @@ function shouldCountToolCall(toolName) {
|
|
|
23
50
|
}
|
|
24
51
|
return true;
|
|
25
52
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
53
|
+
function getUserMessageForKeywordDetection(event) {
|
|
54
|
+
return event.body?.trim() || event.content.trim();
|
|
55
|
+
}
|
|
56
|
+
function shouldNudgeForSelfEvolutionKeyword(text) {
|
|
57
|
+
if (!text) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
return SELF_EVOLUTION_KEYWORD_PATTERNS.some((pattern) => pattern.test(text));
|
|
61
|
+
}
|
|
31
62
|
const plugin = {
|
|
32
63
|
id: "xiaoyi-channel",
|
|
33
64
|
name: "Xiaoyi Channel",
|
|
@@ -37,11 +68,31 @@ const plugin = {
|
|
|
37
68
|
setXYRuntime(api.runtime);
|
|
38
69
|
api.registerChannel({ plugin: xyPlugin });
|
|
39
70
|
api.registerProvider(xiaoyiProvider);
|
|
40
|
-
|
|
41
|
-
|
|
71
|
+
api.on("before_dispatch", async (event, ctx) => {
|
|
72
|
+
const selfEvolutionEnabled = await selfEvolutionManager.isEnabled();
|
|
73
|
+
if (!ctx.sessionKey || !selfEvolutionEnabled) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const userText = getUserMessageForKeywordDetection(event);
|
|
77
|
+
if (!shouldNudgeForSelfEvolutionKeyword(userText)) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
const shouldNudge = toolCallNudgeManager.tryMarkKeywordNudge(ctx.sessionKey);
|
|
82
|
+
api.logger.debug?.(`[SELF_EVOLUTION] Keyword check hit: sessionKey=${ctx.sessionKey}, shouldNudge=${shouldNudge}`);
|
|
83
|
+
if (shouldNudge) {
|
|
84
|
+
api.logger.info?.(`[SELF_EVOLUTION] Keyword-triggered nudge injected: sessionKey=${ctx.sessionKey}`);
|
|
85
|
+
await tryInjectSteer(ctx.sessionKey, SELF_EVOLUTION_KEYWORD_NUDGE_MESSAGE);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
api.logger.error(`[SELF_EVOLUTION] before_dispatch keyword nudge error: ${err}`);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
42
92
|
api.on("after_tool_call", async (event, ctx) => {
|
|
93
|
+
const selfEvolutionEnabled = await selfEvolutionManager.isEnabled();
|
|
43
94
|
if (ctx.sessionKey &&
|
|
44
|
-
|
|
95
|
+
selfEvolutionEnabled &&
|
|
45
96
|
shouldCountToolCall(event.toolName)) {
|
|
46
97
|
try {
|
|
47
98
|
const { count, shouldNudge } = toolCallNudgeManager.recordToolCall(ctx.sessionKey);
|
|
@@ -65,9 +116,8 @@ const plugin = {
|
|
|
65
116
|
if (resultLength <= MIN_TEXT_LENGTH || resultLength > MAX_TOTAL_LENGTH) {
|
|
66
117
|
return;
|
|
67
118
|
}
|
|
68
|
-
// 构造 sentinel_hook 格式的 payload: { tool, output: [{ content }] }
|
|
69
119
|
const questionText = {
|
|
70
|
-
subSceneID:
|
|
120
|
+
subSceneID: "TOOL_OUTPUT",
|
|
71
121
|
tool: event.toolName,
|
|
72
122
|
output: [{ content: "" }],
|
|
73
123
|
};
|
|
@@ -91,17 +141,6 @@ const plugin = {
|
|
|
91
141
|
api.logger.error(`[SENTINEL HOOK] after_tool_call error: ${err}`);
|
|
92
142
|
}
|
|
93
143
|
});
|
|
94
|
-
// SKILL RETRIEVER HOOK: before_prompt_build hook
|
|
95
|
-
const pluginConfig = api.pluginConfig || {};
|
|
96
|
-
const skillRetrieverConfig = normalizeToolRetrieverConfig({
|
|
97
|
-
enabled: pluginConfig.skillRetrieverEnabled ?? true,
|
|
98
|
-
maxTools: pluginConfig.skillRetrieverMaxTools ?? 2,
|
|
99
|
-
includeUninstalledOnly: true,
|
|
100
|
-
envFilePath: "~/.openclaw/.xiaoyienv",
|
|
101
|
-
timeoutMs: pluginConfig.skillRetrieverTimeoutMs ?? 1000,
|
|
102
|
-
});
|
|
103
|
-
const beforePromptBuildHandler = createBeforePromptBuildHandler(skillRetrieverConfig);
|
|
104
|
-
api.on("before_prompt_build", beforePromptBuildHandler);
|
|
105
144
|
},
|
|
106
145
|
};
|
|
107
146
|
export default plugin;
|
package/dist/src/provider.js
CHANGED
|
@@ -12,7 +12,7 @@ import { getCurrentSessionContext } from "./tools/session-manager.js";
|
|
|
12
12
|
import { selfEvolutionManager } from "./utils/self-evolution-manager.js";
|
|
13
13
|
// ── Retry config ──────────────────────────────────────────────
|
|
14
14
|
const RETRY_DELAYS_MS = [10_000, 20_000, 40_000, 60_000];
|
|
15
|
-
const MAX_RETRY_ATTEMPTS =
|
|
15
|
+
const MAX_RETRY_ATTEMPTS = 4;
|
|
16
16
|
/** Check if an errorMessage indicates a retryable provider error by type. */
|
|
17
17
|
function isRetryableProviderError(message) {
|
|
18
18
|
if (!message)
|
|
@@ -22,6 +22,8 @@ function isRetryableProviderError(message) {
|
|
|
22
22
|
return true;
|
|
23
23
|
if (lower.includes("rate limit reached for requests"))
|
|
24
24
|
return true;
|
|
25
|
+
if (lower.includes("现在访问有点拥挤,稍等一下再试会更顺畅哦~"))
|
|
26
|
+
return true;
|
|
25
27
|
return false;
|
|
26
28
|
}
|
|
27
29
|
/** Check if the request is triggered by a cron job by inspecting the first user message. */
|
|
@@ -252,7 +254,7 @@ ${SELF_EVOLUTION_PROMPT_BEGIN}
|
|
|
252
254
|
- 隐式经验与避坑沉淀:当前任务执行过程中,你识别出了可以持久化为长期经验的标准工作流,或者在执行中遇到了工具调用错误、命令参数错误等明确的“踩坑点”,以后需要规避。
|
|
253
255
|
|
|
254
256
|
[触发后处理]
|
|
255
|
-
- 遵循 "self-evolution" 技能指导逐步进行技能评估。
|
|
257
|
+
- 遵循 "xiaoyi-self-evolution" 技能指导逐步进行技能评估。
|
|
256
258
|
- 只能在任务完成后,正式回复用户前读取该技能。
|
|
257
259
|
- 禁止直接修改文件。需要先创建提案,向用户发送进化请求,用户同意后才能进行更改。
|
|
258
260
|
${SELF_EVOLUTION_PROMPT_END}
|
|
@@ -350,6 +352,8 @@ export const xiaoyiProvider = {
|
|
|
350
352
|
const cronTitle = extractCronTitle(context.messages);
|
|
351
353
|
if (cronTitle)
|
|
352
354
|
dynamicHeaders["x-cron-title"] = cronTitle;
|
|
355
|
+
if (context.messages?.length === 1)
|
|
356
|
+
dynamicHeaders["x-cron-flag"] = "begin";
|
|
353
357
|
}
|
|
354
358
|
}
|
|
355
359
|
else {
|
|
@@ -365,6 +369,8 @@ export const xiaoyiProvider = {
|
|
|
365
369
|
const cronTitle = extractCronTitle(context.messages);
|
|
366
370
|
if (cronTitle)
|
|
367
371
|
dynamicHeaders["x-cron-title"] = cronTitle;
|
|
372
|
+
if (context.messages?.length === 1)
|
|
373
|
+
dynamicHeaders["x-cron-flag"] = "begin";
|
|
368
374
|
}
|
|
369
375
|
}
|
|
370
376
|
if (typeof sessionId === "string")
|
|
@@ -6,7 +6,9 @@ declare class ToolCallNudgeManager {
|
|
|
6
6
|
private readonly threshold;
|
|
7
7
|
private readonly sessions;
|
|
8
8
|
constructor(threshold?: number);
|
|
9
|
+
private getSessionState;
|
|
9
10
|
recordToolCall(sessionKey: string): RecordToolCallResult;
|
|
11
|
+
tryMarkKeywordNudge(sessionKey: string): boolean;
|
|
10
12
|
clearSession(sessionKey: string): void;
|
|
11
13
|
}
|
|
12
14
|
export declare const TOOL_CALL_NUDGE_THRESHOLD = 5;
|
|
@@ -5,7 +5,7 @@ class ToolCallNudgeManager {
|
|
|
5
5
|
constructor(threshold = DEFAULT_TOOL_CALL_NUDGE_THRESHOLD) {
|
|
6
6
|
this.threshold = threshold;
|
|
7
7
|
}
|
|
8
|
-
|
|
8
|
+
getSessionState(sessionKey) {
|
|
9
9
|
let state = this.sessions.get(sessionKey);
|
|
10
10
|
if (!state) {
|
|
11
11
|
state = {
|
|
@@ -14,6 +14,10 @@ class ToolCallNudgeManager {
|
|
|
14
14
|
};
|
|
15
15
|
this.sessions.set(sessionKey, state);
|
|
16
16
|
}
|
|
17
|
+
return state;
|
|
18
|
+
}
|
|
19
|
+
recordToolCall(sessionKey) {
|
|
20
|
+
const state = this.getSessionState(sessionKey);
|
|
17
21
|
state.count += 1;
|
|
18
22
|
if (!state.nudged && state.count >= this.threshold) {
|
|
19
23
|
state.nudged = true;
|
|
@@ -27,6 +31,14 @@ class ToolCallNudgeManager {
|
|
|
27
31
|
shouldNudge: false,
|
|
28
32
|
};
|
|
29
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
|
+
}
|
|
30
42
|
clearSession(sessionKey) {
|
|
31
43
|
this.sessions.delete(sessionKey);
|
|
32
44
|
}
|