@ynhcj/xiaoyi-channel 0.0.209-next → 0.0.210-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
@@ -12,19 +12,7 @@ import { createBeforePromptBuildHandler } from "./src/skill-retriever/hooks.js";
12
12
  import { normalizeToolRetrieverConfig } from "./src/skill-retriever/config.js";
13
13
  import { registerCLIHook } from "./src/tools/hmos-cli.js";
14
14
  import { writeSkillUsage } from "./src/utils/skills-logger.js";
15
- import { getSteerQueue } from "./src/steer-queue.js";
16
15
  import { logger } from "./src/utils/logger.js";
17
- import fs from "node:fs";
18
- import path from "node:path";
19
- // Diagnostic: write marker file to confirm hook fires
20
- function touchHookMarker(label) {
21
- try {
22
- const dir = "/tmp/xy-steer-diag";
23
- fs.mkdirSync(dir, { recursive: true });
24
- fs.writeFileSync(path.join(dir, `${Date.now()}_${label}.txt`), `${new Date().toISOString()} ${label}\n`);
25
- }
26
- catch { }
27
- }
28
16
  /**
29
17
  * Parse a file path string to detect if it refers to a SKILL.md file within
30
18
  * a skills directory. Returns the skill name (parent directory) if so.
@@ -228,43 +216,10 @@ function registerFullHooks(api) {
228
216
  envFilePath: "~/.openclaw/.xiaoyienv",
229
217
  timeoutMs: pluginConfig.skillRetrieverTimeoutMs ?? 1000,
230
218
  });
231
- const baseBeforePromptBuildHandler = createBeforePromptBuildHandler(skillRetrieverConfig);
232
- // STEER INJECTION: merged into the before_prompt_build handler so it runs
233
- // in the same hook registration call that we know already works (skill-retriever).
234
- // This avoids any issues with separate api.on() registrations not being
235
- // picked up by the global hook runner.
236
- touchHookMarker("register_hook");
219
+ const beforePromptBuildHandler = createBeforePromptBuildHandler(skillRetrieverConfig);
237
220
  api.on("before_prompt_build", async (event, ctx) => {
238
221
  logger.log(`[BEFORE_PROMPT_BUILD] hook fired, sessionKey=${ctx.sessionKey || "undefined"}, sessionId=${ctx.sessionId || "undefined"}`);
239
- // Run skill-retriever first
240
- const baseResult = await baseBeforePromptBuildHandler(event, ctx);
241
- // Check for pending steer messages queued by bot.ts.
242
- const sessionKey = ctx.sessionKey;
243
- const queue = getSteerQueue();
244
- const pending = sessionKey ? queue.get(sessionKey) : undefined;
245
- const allKeys = Array.from(queue.keys());
246
- // Diagnostic: write state to marker file for each hook fire
247
- touchHookMarker(`hook_sessionKey=${sessionKey || "undefined"}_` +
248
- `pendingLen=${pending?.length ?? 0}_` +
249
- `queueSize=${queue.size}_` +
250
- `queueKeys=${allKeys.join(",") || "empty"}`);
251
- if (!sessionKey || !pending || pending.length === 0)
252
- return baseResult;
253
- // Drain pending steer messages
254
- queue.delete(sessionKey);
255
- const steerText = pending.splice(0).join("\n\n");
256
- const steerAppend = `\n用户追加诉求:${steerText}`;
257
- logger.log(`[STEER-HOOK] Injecting steer: sessionKey=${sessionKey} textLen=${steerText.length}`);
258
- touchHookMarker(`steer_ok_${sessionKey}`);
259
- // Merge steer appendContext with skill-retriever's result
260
- const merged = { ...(baseResult ?? {}) };
261
- if (merged.appendContext) {
262
- merged.appendContext = `${merged.appendContext}\n\n${steerAppend}`;
263
- }
264
- else {
265
- merged.appendContext = steerAppend;
266
- }
267
- return merged;
222
+ return beforePromptBuildHandler(event, ctx);
268
223
  });
269
224
  registerSelfEvolutionToolResultNudge(api);
270
225
  }
package/dist/src/bot.js CHANGED
@@ -15,7 +15,6 @@ import { selfEvolutionManager } from "./utils/self-evolution-manager.js";
15
15
  import { saveRuntimeInfo } from "./utils/runtime-manager.js";
16
16
  import { toolCallNudgeManager } from "./utils/tool-call-nudge-manager.js";
17
17
  import { setCsplSteerContext } from "./cspl/steer-context.js";
18
- import { getSteerQueue } from "./steer-queue.js";
19
18
  import { registerTaskId, decrementTaskIdRef, hasActiveTask, } from "./task-manager.js";
20
19
  import { logger } from "./utils/logger.js";
21
20
  /**
@@ -315,12 +314,17 @@ export async function handleXYMessage(params) {
315
314
  envelope: envelopeOptions,
316
315
  body: messageBody,
317
316
  });
317
+ // 🔑 Steer messages use /steer prefix to trigger the native slash command
318
+ // fast path in get-reply, which calls handleSteerCommand → queueEmbedded-
319
+ // AgentMessageWithOutcomeAsync without going through admitReplyTurn or
320
+ // the isStreaming guard in runReplyAgent.
321
+ const steerCommandBody = isUpdate ? `/steer ${textForAgent}` : textForAgent;
318
322
  // ✅ Finalize inbound context (following feishu pattern)
319
323
  // Use route.accountId and route.sessionKey instead of parsed fields
320
324
  const ctxPayload = core.channel.reply.finalizeInboundContext({
321
325
  Body: body,
322
- RawBody: textForAgent,
323
- CommandBody: textForAgent,
326
+ RawBody: steerCommandBody,
327
+ CommandBody: steerCommandBody,
324
328
  From: parsed.sessionId,
325
329
  To: parsed.sessionId, // ✅ Simplified: use sessionId as target (context is managed by SessionKey)
326
330
  SessionKey: route.sessionKey, // ✅ Use route.sessionKey
@@ -340,41 +344,6 @@ export async function handleXYMessage(params) {
340
344
  ReplyToBody: undefined, // A2A protocol doesn't support reply/quote
341
345
  ...mediaPayload,
342
346
  });
343
- // 🔑 Steer bypass: queue the message for agent_turn_prepare hook injection
344
- // instead of going through dispatchReplyFromConfig which blocks on admitReplyTurn
345
- // waiting for the active reply operation to complete.
346
- //
347
- // This covers BOTH:
348
- // 1. Normal A2A steer (isUpdate && !skipReg) — user sends a message during
349
- // an active agent run
350
- // 2. CSPL/self-evolution steer (isUpdate && skipReg) — hook-triggered
351
- // injection via tryInjectSteer. Without the bypass, CSPL steer would
352
- // deadlock: it runs inside the agent turn, so admitReplyTurn's
353
- // waitForIdle waits for the reply operation to finish, but the
354
- // operation is owned by the very same agent turn.
355
- //
356
- // The hook fires before every agent prompt build, so the steer text reaches
357
- // the model on the next turn without blocking.
358
- if (isUpdate) {
359
- const steerQueue = getSteerQueue();
360
- // Use route.sessionKey as the queue key. The hook looks up by
361
- // ctx.sessionKey which is the same value (agent:main:direct:<peerId>).
362
- // Do NOT use parsed.sessionId because ctx.sessionId in the hook
363
- // context is OpenClaw's internal random UUID, not the A2A sessionId.
364
- const queueKey = route.sessionKey;
365
- const pending = steerQueue.get(queueKey) ?? [];
366
- pending.push(textForAgent);
367
- steerQueue.set(queueKey, pending);
368
- log.log(`[BOT] Steer bypass: queued for agent_turn_prepare hook, queueKey=${queueKey}, sessionKey=${route.sessionKey}, queueDepth=${pending.length}, skipReg=${skipReg}`);
369
- // Only decrement refCount when registerTaskId was called (non-skipReg).
370
- // skipReg callers (CSPL/self-evolution) don't increment refCount.
371
- if (!skipReg) {
372
- decrementTaskIdRef(parsed.sessionId);
373
- }
374
- // Release the global dispatch init gate — steer doesn't need it.
375
- params.onInitComplete?.();
376
- return;
377
- }
378
347
  // 🔑 For steer messages, pre-set steered=true so the dispatcher skips final
379
348
  // response and cleanup — the first message's dispatcher handles those.
380
349
  const steerState = { steered: isUpdate };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ynhcj/xiaoyi-channel",
3
- "version": "0.0.209-next",
3
+ "version": "0.0.210-next",
4
4
  "description": "OpenClaw Xiaoyi Channel plugin - Xiaoyi A2A protocol integration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",