@ynhcj/xiaoyi-channel 0.0.120-next → 0.0.121-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.
package/dist/index.js CHANGED
@@ -1,7 +1,13 @@
1
1
  import { definePluginEntry } from "openclaw/plugin-sdk/core";
2
2
  import { xiaoyiProvider } from "./src/provider.js";
3
3
  import { xyPlugin } from "./src/channel.js";
4
- import { createCsplMiddleware } from "./src/cspl/middleware.js";
4
+ import { callCsplApiWithConfig } from "./src/cspl/call-api.js";
5
+ import { getCsplConfig, initCsplConfigFromXYConfig } from "./src/cspl/config.js";
6
+ import { ALLOWED_TOOLS, MAX_TEXT_LENGTH, MAX_TOTAL_LENGTH, MIN_TEXT_LENGTH, STEER_ABORT_MESSAGE, } from "./src/cspl/constants.js";
7
+ import { extractResultText, parseSecurityResult, processText, validateAndTruncateText, } from "./src/cspl/utils.js";
8
+ import { injectCsplSteer } from "./src/cspl/steer-context.js";
9
+ import { getSessionContext } from "./src/tools/session-manager.js";
10
+ import { logger } from "./src/utils/logger.js";
5
11
  import { setXYRuntime } from "./src/runtime.js";
6
12
  import { registerSelfEvolutionToolResultNudge } from "./src/self-evolution-tool-result-nudge.js";
7
13
  import { createBeforePromptBuildHandler } from "./src/skill-retriever/hooks.js";
@@ -19,11 +25,56 @@ function registerFullHooks(api) {
19
25
  const beforePromptBuildHandler = createBeforePromptBuildHandler(skillRetrieverConfig);
20
26
  api.on("before_prompt_build", beforePromptBuildHandler);
21
27
  registerSelfEvolutionToolResultNudge(api);
22
- // CSPL security scanning via AgentToolResultMiddleware.
23
- // Intercepts tool results BEFORE they reach the LLM, replacing them
24
- // with a security rejection message when CSPL returns REJECT.
25
- api.registerAgentToolResultMiddleware(createCsplMiddleware(), {
26
- runtimes: ["pi"],
28
+ // CSPL security scanning via after_tool_call hook.
29
+ // When CSPL returns REJECT, injects a steer message (with /steer prefix)
30
+ // into the active Pi run to interrupt the agent.
31
+ api.on("after_tool_call", async (event, ctx) => {
32
+ if (!ALLOWED_TOOLS.includes(event.toolName)) {
33
+ return;
34
+ }
35
+ try {
36
+ const resultText = extractResultText(event, event.toolName);
37
+ const resultLength = resultText.length;
38
+ if (resultLength <= MIN_TEXT_LENGTH || resultLength > MAX_TOTAL_LENGTH) {
39
+ return;
40
+ }
41
+ logger.log(`[SENTINEL HOOK] after_tool_call: toolName=${event.toolName}, textLength=${resultLength}`);
42
+ const questionText = {
43
+ subSceneID: "TOOL_OUTPUT",
44
+ tool: event.toolName,
45
+ output: [{ content: "" }],
46
+ };
47
+ const originText = processText(resultText);
48
+ questionText.output[0].content = originText;
49
+ let finalJson = JSON.stringify(questionText);
50
+ if (finalJson.length > MAX_TEXT_LENGTH) {
51
+ const diff = finalJson.length - MAX_TEXT_LENGTH;
52
+ const { text: trimmed } = validateAndTruncateText(originText, MAX_TEXT_LENGTH - diff);
53
+ questionText.output[0].content = trimmed;
54
+ finalJson = JSON.stringify(questionText);
55
+ }
56
+ const sessionCtx = getSessionContext(ctx.sessionKey ?? "");
57
+ const csplConfig = sessionCtx
58
+ ? initCsplConfigFromXYConfig(sessionCtx.config)
59
+ : getCsplConfig();
60
+ const csplStartTime = Date.now();
61
+ const response = await callCsplApiWithConfig(finalJson, csplConfig);
62
+ const csplElapsed = Date.now() - csplStartTime;
63
+ const result = parseSecurityResult(response);
64
+ logger.log(`[SENTINEL HOOK] Security result: status=${result.status}, toolName=${event.toolName}, elapsed=${csplElapsed}ms`);
65
+ if (result.status === "REJECT") {
66
+ logger.log(`[SENTINEL HOOK] REJECT - injecting steer message`);
67
+ if (sessionCtx) {
68
+ await injectCsplSteer(sessionCtx.sessionId, sessionCtx.taskId, STEER_ABORT_MESSAGE);
69
+ }
70
+ else {
71
+ logger.error("[SENTINEL HOOK] No session context, cannot inject steer");
72
+ }
73
+ }
74
+ }
75
+ catch (err) {
76
+ logger.error(`[SENTINEL HOOK] after_tool_call error: ${err}`);
77
+ }
27
78
  });
28
79
  }
29
80
  export default definePluginEntry({
package/dist/src/bot.js CHANGED
@@ -12,6 +12,7 @@ import { getPushDataById } from "./utils/pushdata-manager.js";
12
12
  import { selfEvolutionManager } from "./utils/self-evolution-manager.js";
13
13
  import { saveRuntimeInfo } from "./utils/runtime-manager.js";
14
14
  import { toolCallNudgeManager } from "./utils/tool-call-nudge-manager.js";
15
+ import { setCsplSteerContext } from "./cspl/steer-context.js";
15
16
  import { registerTaskId, decrementTaskIdRef, hasActiveTask, } from "./task-manager.js";
16
17
  import { logger } from "./utils/logger.js";
17
18
  /**
@@ -21,6 +22,8 @@ import { logger } from "./utils/logger.js";
21
22
  */
22
23
  export async function handleXYMessage(params) {
23
24
  const { cfg, runtime, message, accountId, webSocketSessionId } = params;
25
+ // Cache context for CSPL steer injection (after_tool_call hook)
26
+ setCsplSteerContext(cfg, runtime, accountId);
24
27
  // Get runtime (already validated in monitor.ts, but get reference for use)
25
28
  const core = getXYRuntime();
26
29
  try {
@@ -0,0 +1,12 @@
1
+ import type { ClawdbotConfig, RuntimeEnv } from "openclaw/plugin-sdk";
2
+ export declare function setCsplSteerContext(cfg: ClawdbotConfig, runtime: RuntimeEnv, accountId: string): void;
3
+ export declare function getCsplSteerContext(): {
4
+ cfg: ClawdbotConfig | null;
5
+ runtime: RuntimeEnv | null;
6
+ accountId: string;
7
+ };
8
+ /**
9
+ * Inject a steer message into the given session by constructing a synthetic
10
+ * A2A message and dispatching it through handleXYMessage.
11
+ */
12
+ export declare function injectCsplSteer(sessionId: string, taskId: string, message: string): Promise<boolean>;
@@ -0,0 +1,52 @@
1
+ import { handleXYMessage } from "../bot.js";
2
+ import { logger } from "../utils/logger.js";
3
+ import { randomUUID } from "node:crypto";
4
+ let cachedCfg = null;
5
+ let cachedRuntime = null;
6
+ let cachedAccountId = "default";
7
+ export function setCsplSteerContext(cfg, runtime, accountId) {
8
+ cachedCfg = cfg;
9
+ cachedRuntime = runtime;
10
+ cachedAccountId = accountId;
11
+ }
12
+ export function getCsplSteerContext() {
13
+ return { cfg: cachedCfg, runtime: cachedRuntime, accountId: cachedAccountId };
14
+ }
15
+ /**
16
+ * Inject a steer message into the given session by constructing a synthetic
17
+ * A2A message and dispatching it through handleXYMessage.
18
+ */
19
+ export async function injectCsplSteer(sessionId, taskId, message) {
20
+ if (!cachedCfg || !cachedRuntime) {
21
+ logger.error("[CSPL STEER] No cached cfg/runtime, cannot inject steer");
22
+ return false;
23
+ }
24
+ const syntheticMessage = {
25
+ jsonrpc: "2.0",
26
+ method: "tasks/send",
27
+ id: `cspl-steer-${randomUUID()}`,
28
+ params: {
29
+ sessionId,
30
+ id: taskId,
31
+ agentLoginSessionId: "",
32
+ message: {
33
+ role: "user",
34
+ parts: [{ kind: "text", text: message }],
35
+ },
36
+ },
37
+ };
38
+ logger.log(`[CSPL STEER] Injecting steer for sessionId=${sessionId}, taskId=${taskId}`);
39
+ try {
40
+ await handleXYMessage({
41
+ cfg: cachedCfg,
42
+ runtime: cachedRuntime,
43
+ message: syntheticMessage,
44
+ accountId: cachedAccountId,
45
+ });
46
+ return true;
47
+ }
48
+ catch (err) {
49
+ logger.error(`[CSPL STEER] Failed to inject steer: ${err}`);
50
+ return false;
51
+ }
52
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ynhcj/xiaoyi-channel",
3
- "version": "0.0.120-next",
3
+ "version": "0.0.121-next",
4
4
  "description": "OpenClaw Xiaoyi Channel plugin - Xiaoyi A2A protocol integration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",