@ynhcj/xiaoyi-channel 0.0.120-beta → 0.0.122-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,11 @@
1
1
  import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
2
- declare const plugin: {
2
+ declare const _default: {
3
3
  id: string;
4
4
  name: string;
5
5
  description: string;
6
- configSchema: import("openclaw/plugin-sdk").OpenClawPluginConfigSchema;
7
- register(api: OpenClawPluginApi): void;
6
+ configSchema: import("openclaw/plugin-sdk").ChannelConfigSchema;
7
+ register: (api: OpenClawPluginApi) => void;
8
+ channelPlugin: import("openclaw/plugin-sdk").ChannelPlugin;
9
+ setChannelRuntime?: (runtime: import("openclaw/plugin-sdk").PluginRuntime) => void;
8
10
  };
9
- export default plugin;
11
+ export default _default;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
1
+ import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
2
2
  import { xiaoyiProvider } from "./src/provider.js";
3
3
  import { xyPlugin } from "./src/channel.js";
4
4
  import { callCsplApi } from "./src/cspl/call-api.js";
@@ -6,31 +6,16 @@ import { ALLOWED_TOOLS, MAX_TEXT_LENGTH, MAX_TOTAL_LENGTH, MIN_TEXT_LENGTH, STEE
6
6
  import { extractResultText, parseSecurityResult, processText, validateAndTruncateText, } from "./src/cspl/utils.js";
7
7
  import { setXYRuntime } from "./src/runtime.js";
8
8
  import { tryInjectSteer } from "./src/steer-injector.js";
9
- import { selfEvolutionManager } from "./src/utils/self-evolution-manager.js";
10
- import { TOOL_CALL_NUDGE_THRESHOLD, toolCallNudgeManager, } from "./src/utils/tool-call-nudge-manager.js";
9
+ import { registerSelfEvolutionToolResultNudge } from "./src/self-evolution-tool-result-nudge.js";
11
10
  import { createBeforePromptBuildHandler } from "./src/skill-retriever/hooks.js";
12
11
  import { normalizeToolRetrieverConfig } from "./src/skill-retriever/config.js";
13
- const SELF_EVOLUTION_NUDGE_MESSAGE = "[系统消息,非用户发言,无需主动告知用户] 当前任务已经调用了较多次数的工具。请先完成用户主要任务。完成后,如果这些工具调用暴露出稳定、通用、可复用的流程、排错经验或踩坑点,再考虑阅读 xiaoyi-self-evolution 技能进行评估;如果没有高价值候选经验,请忽略本提醒。";
14
- function shouldCountToolCall(toolName) {
15
- if (toolName === "save_self_evolution_skill") {
16
- return false;
17
- }
18
- if (toolName === "call_device_tool") {
19
- return false;
20
- }
21
- if (toolName.endsWith("_tool_schema")) {
22
- return false;
23
- }
24
- return true;
25
- }
26
- const plugin = {
12
+ export default defineChannelPluginEntry({
27
13
  id: "xiaoyi-channel",
28
14
  name: "Xiaoyi Channel",
29
15
  description: "Xiaoyi channel plugin - Xiaoyi A2A protocol integration",
30
- configSchema: emptyPluginConfigSchema(),
31
- register(api) {
32
- setXYRuntime(api.runtime);
33
- api.registerChannel({ plugin: xyPlugin });
16
+ plugin: xyPlugin,
17
+ setRuntime: setXYRuntime,
18
+ registerFull(api) {
34
19
  api.registerProvider(xiaoyiProvider);
35
20
  // SKILL RETRIEVER HOOK: before_prompt_build hook
36
21
  const pluginConfig = api.pluginConfig || {};
@@ -43,21 +28,8 @@ const plugin = {
43
28
  });
44
29
  const beforePromptBuildHandler = createBeforePromptBuildHandler(skillRetrieverConfig);
45
30
  api.on("before_prompt_build", beforePromptBuildHandler);
31
+ registerSelfEvolutionToolResultNudge(api);
46
32
  api.on("after_tool_call", async (event, ctx) => {
47
- const selfEvolutionEnabled = await selfEvolutionManager.isEnabled();
48
- if (ctx.sessionKey && selfEvolutionEnabled && shouldCountToolCall(event.toolName)) {
49
- try {
50
- const { count, shouldNudge } = toolCallNudgeManager.recordToolCall(ctx.sessionKey);
51
- api.logger.debug?.(`[SELF_EVOLUTION] Tool call counted: tool=${event.toolName}, count=${count}, threshold=${TOOL_CALL_NUDGE_THRESHOLD}, sessionKey=${ctx.sessionKey}`);
52
- if (shouldNudge) {
53
- api.logger.info?.(`[SELF_EVOLUTION] Tool call threshold reached, injecting nudge: count=${count}, sessionKey=${ctx.sessionKey}`);
54
- await tryInjectSteer(ctx.sessionKey, SELF_EVOLUTION_NUDGE_MESSAGE);
55
- }
56
- }
57
- catch (err) {
58
- api.logger.error(`[SELF_EVOLUTION] after_tool_call nudge error: ${err}`);
59
- }
60
- }
61
33
  if (!ALLOWED_TOOLS.includes(event.toolName)) {
62
34
  return;
63
35
  }
@@ -94,5 +66,4 @@ const plugin = {
94
66
  }
95
67
  });
96
68
  },
97
- };
98
- export default plugin;
69
+ });
@@ -1,3 +1,3 @@
1
- import type { ProviderPlugin } from "openclaw/plugin-sdk/provider-models";
1
+ import type { ProviderPlugin } from "openclaw/plugin-sdk/provider-model-shared";
2
2
  export declare function applySelfEvolutionPrompt(systemPrompt: string | undefined, enabled: boolean): string;
3
3
  export declare const xiaoyiProvider: ProviderPlugin;
@@ -370,6 +370,23 @@ function encodeUid(uid) {
370
370
  function getUidFromConfig(config) {
371
371
  return config?.channels?.["xiaoyi-channel"]?.uid;
372
372
  }
373
+ /**
374
+ * Trim user message metadata:
375
+ * 1. In "Conversation info (untrusted metadata)" JSON, keep only timestamp
376
+ * 2. Remove "Sender (untrusted metadata)" section entirely
377
+ */
378
+ function trimUserMetadata(text) {
379
+ // 1. Conversation info: keep only timestamp
380
+ text = text.replace(/(Conversation info \(untrusted metadata\):\n```json\n)([\s\S]*?)(\n```)/, (_match, prefix, json, suffix) => {
381
+ const tsMatch = json.match(/"timestamp"\s*:\s*"([^"]+)"/);
382
+ return tsMatch
383
+ ? `${prefix}{\n "timestamp": "${tsMatch[1]}"\n}\n${suffix}`
384
+ : _match;
385
+ });
386
+ // 2. Sender: remove entirely
387
+ text = text.replace(/\n*Sender \(untrusted metadata\):\n```json\n[\s\S]*?\n```\n*/, "\n");
388
+ return text.replace(/\n{3,}/g, "\n\n");
389
+ }
373
390
  export const xiaoyiProvider = {
374
391
  id: "xiaoyiprovider",
375
392
  label: "Xiaoyi Provider",
@@ -509,6 +526,23 @@ export const xiaoyiProvider = {
509
526
  const deviceSection = `\n\n## Current User Device Context\nThe current user is using the following device: ${displayDevice}\nYou need to be aware of the user's current device and provide guidance accordingly. If the response involves device-related tools or actions, you must tailor the reply based on the user's current device, using device-specific references such as "saved to the Notes/Calendar on your {deviceType}.\n"`;
510
527
  context.systemPrompt = (context.systemPrompt ?? "") + deviceSection;
511
528
  }
529
+ // ── Trim user message metadata ──────────────────────
530
+ if (context.messages) {
531
+ for (const msg of context.messages) {
532
+ if (msg.role !== "user" || !msg.content)
533
+ continue;
534
+ if (typeof msg.content === "string") {
535
+ msg.content = trimUserMetadata(msg.content);
536
+ }
537
+ else if (Array.isArray(msg.content)) {
538
+ for (const block of msg.content) {
539
+ if (block.type === "text" && typeof block.text === "string") {
540
+ block.text = trimUserMetadata(block.text);
541
+ }
542
+ }
543
+ }
544
+ }
545
+ }
512
546
  // ── Retry-capable streaming ──────────────────────────────
513
547
  const cronJob = isCronTriggered(context.messages);
514
548
  if (cronJob)
@@ -1,11 +1,3 @@
1
- import type { PluginRuntime } from "openclaw/plugin-sdk";
2
- /**
3
- * Set the Xiaoyi channel runtime instance.
4
- * This should be called once during plugin initialization.
5
- */
6
- export declare function setXYRuntime(next: PluginRuntime): void;
7
- /**
8
- * Get the current Xiaoyi channel runtime instance.
9
- * Throws an error if the runtime has not been initialized.
10
- */
11
- export declare function getXYRuntime(): PluginRuntime;
1
+ import type { PluginRuntime } from "openclaw/plugin-sdk/core";
2
+ declare const setXYRuntime: (next: PluginRuntime) => void, getXYRuntime: () => PluginRuntime;
3
+ export { getXYRuntime, setXYRuntime };
@@ -1,18 +1,6 @@
1
- let runtime = null;
2
- /**
3
- * Set the Xiaoyi channel runtime instance.
4
- * This should be called once during plugin initialization.
5
- */
6
- export function setXYRuntime(next) {
7
- runtime = next;
8
- }
9
- /**
10
- * Get the current Xiaoyi channel runtime instance.
11
- * Throws an error if the runtime has not been initialized.
12
- */
13
- export function getXYRuntime() {
14
- if (!runtime) {
15
- throw new Error("Xiaoyi runtime not initialized. Call setXYRuntime() first.");
16
- }
17
- return runtime;
18
- }
1
+ import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
2
+ const { setRuntime: setXYRuntime, getRuntime: getXYRuntime } = createPluginRuntimeStore({
3
+ pluginId: "xiaoyi-channel",
4
+ errorMessage: "Xiaoyi runtime not initialized. Call setXYRuntime() first.",
5
+ });
6
+ export { getXYRuntime, setXYRuntime };
@@ -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
+ }
@@ -1,22 +1,13 @@
1
1
  import { searchTools, formatToolsForContext, extractUserQuery } from "./tool-search.js";
2
- const TOOL_RETRIEVER_HEADER = `## 用户查询相关skill列表如下:
2
+ const TOOL_RETRIEVER_HEADER = `[系统消息,非用户发言]
3
3
 
4
4
  `;
5
5
  const TOOL_RETRIEVER_FOOTER = `
6
- 以上是检索到的、与当前查询相关但用户尚未安装的skill,请按照以下规则判断是否需要推荐:
7
- 1.判断用户当前请求的意图类型:
8
- -若用户请求为查询已有信息、查看状态、执行已有功能或进行常规操作(例如查看定时任务列表),且这些操作无需额外skill即可完成,则不推荐任何skill。
9
- 2.仅在以下条件全部满足时,才考虑推荐skill:
10
- -用户请求明确表示需要完成某个具体任务;
11
- -现有能力(包括已安装的skill或系统自带功能)不足以满足该任务。此时,优先推荐与任务强相关的skill。
12
- 3.对于强相关且用户尚未安装的skill:
13
- -可主动提醒用户考虑安装,但禁止直接安装;
14
- -用户明确同意后,使用find-skills执行安装。
15
- 4.若用户已安装的skill已能很好地完成当前任务,即使存在功能相似的未安装skill,也无需提醒。
6
+ 以上是检索到的、与当前用户请求相关但用户尚未安装的skill,请按照TOOLS.md中find-skills中的[隐式推荐触发规则]来判断是否要给用户推荐。
16
7
  ---以下是用户原始请求---
17
8
  `;
18
9
  const PLUGIN_LOG_PREFIX = "[skill-retriever]";
19
- const SKIP_KEYWORDS = ["安装", "装一下", "下载", "查询", "查找", "install", "卸载", "删除", "重载", "定时任务", "重装"];
10
+ const SKIP_KEYWORDS = ["安装", "装一下", "下载", "查询", "查找", "install", "卸载", "删除", "重载", "定时任务", "重装", "进化"];
20
11
  const SKIP_PATTERNS = [
21
12
  "/new", "/reset", "/compact", "/stop", "/think", "/model", "/fast", "/verbose", "/config", "/debug", "/status", "/tasks", "/whoami", "/context", "/skill", "/commands", "/tools"
22
13
  ];
@@ -12,6 +12,9 @@ export function extractUserQuery(fullPrompt) {
12
12
  if (!afterLastNewline || afterLastNewline === "```") {
13
13
  return "";
14
14
  }
15
+ if (fullPrompt.toLowerCase().includes("cron")) {
16
+ return "";
17
+ }
15
18
  return afterLastNewline;
16
19
  }
17
20
  function expandPath(filePath) {
@@ -239,7 +239,7 @@ d. 返回图像理解的文本描述内容`,
239
239
  },
240
240
  remoteUrl: {
241
241
  type: "string",
242
- description: "公网图片地址(可选),公网图片地址(HTTP/HTTPS URL",
242
+ description: "公网图片地址(可选),公网图片地址(HTTP/HTTPS URL),注意不要对原始url做任何截断(例如裁减掉链接后面的鉴权信息或者修改域名后缀),必须使用上下文中完整的图片地址",
243
243
  },
244
244
  prompt: {
245
245
  type: "string",
@@ -21,7 +21,7 @@ class ToolInputError extends Error {
21
21
  export const saveFileToPhoneTool = {
22
22
  name: "save_file_to_file_manager",
23
23
  label: "Save File to Phone",
24
- description: `将文件保存到手机文件管理器。
24
+ description: `将文件保存到用户设备的文件管理器中,通常用户表述为'帮我保存到文管','保存到文件管理'。
25
25
 
26
26
  注意:
27
27
  a. 操作超时时间为60秒,请勿重复调用此工具
@@ -114,7 +114,7 @@ b. 操作超时时间为2分钟(120秒),请勿重复调用此工具,如
114
114
  description: "本地文件路径数组,包含用户需要回传的文件在本地的地址",
115
115
  },
116
116
  fileRemoteUrls: {
117
- description: "公网地址数组,包含用户需要回传的文件的公网地址(会先下载到本地再发送)",
117
+ description: "公网地址数组,包含用户需要回传的文件的公网地址(会先下载到本地再发送),注意不要对原始url做任何截断(例如裁减掉链接后面的鉴权信息或者修改域名后缀),必须使用上下文中完整的文件地址",
118
118
  },
119
119
  },
120
120
  },
@@ -1,4 +1,9 @@
1
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;
2
7
  isEnabled(): Promise<boolean>;
3
8
  }
4
9
  export declare const selfEvolutionManager: SelfEvolutionManager;
@@ -1,4 +1,5 @@
1
- import fs from "node:fs/promises";
1
+ import fs from "node:fs";
2
+ import fsp from "node:fs/promises";
2
3
  const SELF_EVOLUTION_ENV_FILE = "/home/sandbox/.openclaw/.xiaoyiruntime";
3
4
  const SELF_EVOLUTION_ENV_KEY = "selfEvolutionState";
4
5
  function parseBooleanLike(value) {
@@ -12,32 +13,52 @@ function parseBooleanLike(value) {
12
13
  return null;
13
14
  }
14
15
  class SelfEvolutionManager {
15
- async isEnabled() {
16
+ /**
17
+ * Synchronous read for hot paths (e.g. tool_result_persist — must not return a Promise).
18
+ */
19
+ isEnabledSync() {
16
20
  try {
17
- const envData = await fs.readFile(SELF_EVOLUTION_ENV_FILE, "utf-8");
18
- for (const line of envData.split(/\r?\n/u)) {
19
- const trimmed = line.trim();
20
- if (!trimmed || trimmed.startsWith("#")) {
21
- continue;
22
- }
23
- const eqIndex = trimmed.indexOf("=");
24
- if (eqIndex === -1) {
25
- continue;
26
- }
27
- const key = trimmed.slice(0, eqIndex).trim();
28
- if (key !== SELF_EVOLUTION_ENV_KEY) {
29
- continue;
30
- }
31
- const value = trimmed.slice(eqIndex + 1).trim();
32
- const parsed = parseBooleanLike(value);
33
- if (parsed !== null) {
34
- return parsed;
35
- }
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);
36
28
  }
37
29
  return false;
38
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
+ }
39
59
  catch (error) {
40
- if (error?.code !== "ENOENT") {
60
+ const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
61
+ if (code !== "ENOENT") {
41
62
  console.error(`[SELF_EVOLUTION] Failed to read ${SELF_EVOLUTION_ENV_FILE}:`, error);
42
63
  }
43
64
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ynhcj/xiaoyi-channel",
3
- "version": "0.0.120-beta",
3
+ "version": "0.0.122-beta",
4
4
  "description": "OpenClaw Xiaoyi Channel plugin - Xiaoyi A2A protocol integration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -49,7 +49,7 @@
49
49
  }
50
50
  },
51
51
  "peerDependencies": {
52
- "openclaw": ">=2026.3.24"
52
+ "openclaw": ">=2026.5.6"
53
53
  },
54
54
  "peerDependenciesMeta": {
55
55
  "openclaw": {