@ynhcj/xiaoyi-channel 0.0.203-next → 0.0.204-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 +25 -0
- package/dist/src/bot.js +31 -0
- package/dist/src/steer-queue.d.ts +1 -0
- package/dist/src/steer-queue.js +9 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12,6 +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";
|
|
15
16
|
/**
|
|
16
17
|
* Parse a file path string to detect if it refers to a SKILL.md file within
|
|
17
18
|
* a skills directory. Returns the skill name (parent directory) if so.
|
|
@@ -218,6 +219,30 @@ function registerFullHooks(api) {
|
|
|
218
219
|
const beforePromptBuildHandler = createBeforePromptBuildHandler(skillRetrieverConfig);
|
|
219
220
|
api.on("before_prompt_build", beforePromptBuildHandler);
|
|
220
221
|
registerSelfEvolutionToolResultNudge(api);
|
|
222
|
+
// STEER INJECTION HOOK: reads pending steer messages queued by bot.ts
|
|
223
|
+
// and injects them as appendContext on the next agent turn.
|
|
224
|
+
// This bypasses dispatchReplyFromConfig's admitReplyTurn blocking.
|
|
225
|
+
//
|
|
226
|
+
// appendContext places the steer after the current prompt context (which
|
|
227
|
+
// includes the original user query + tool results), so the model sees:
|
|
228
|
+
// [original context]\n\n用户追加诉求:<steer text>
|
|
229
|
+
//
|
|
230
|
+
// Key is deleted BEFORE splice to avoid a race: if bot.ts pushes a new
|
|
231
|
+
// message between splice and delete, it would be lost when delete removes
|
|
232
|
+
// the (now-repopulated) entry. With delete-first, bot.ts always creates a
|
|
233
|
+
// fresh entry for new messages.
|
|
234
|
+
api.on("agent_turn_prepare", async (_event, ctx) => {
|
|
235
|
+
const sessionKey = ctx.sessionKey;
|
|
236
|
+
if (!sessionKey)
|
|
237
|
+
return;
|
|
238
|
+
const queue = getSteerQueue();
|
|
239
|
+
const pending = queue.get(sessionKey);
|
|
240
|
+
if (!pending || pending.length === 0)
|
|
241
|
+
return;
|
|
242
|
+
queue.delete(sessionKey); // delete first to prevent race
|
|
243
|
+
const text = pending.splice(0).join("\n\n");
|
|
244
|
+
return { appendContext: `\n用户追加诉求:${text}` };
|
|
245
|
+
});
|
|
221
246
|
}
|
|
222
247
|
const pluginEntry = definePluginEntry({
|
|
223
248
|
id: "xiaoyi-channel",
|
package/dist/src/bot.js
CHANGED
|
@@ -15,6 +15,7 @@ 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";
|
|
18
19
|
import { registerTaskId, decrementTaskIdRef, hasActiveTask, } from "./task-manager.js";
|
|
19
20
|
import { logger } from "./utils/logger.js";
|
|
20
21
|
/**
|
|
@@ -339,6 +340,36 @@ export async function handleXYMessage(params) {
|
|
|
339
340
|
ReplyToBody: undefined, // A2A protocol doesn't support reply/quote
|
|
340
341
|
...mediaPayload,
|
|
341
342
|
});
|
|
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
|
+
const pending = steerQueue.get(route.sessionKey) ?? [];
|
|
361
|
+
pending.push(textForAgent);
|
|
362
|
+
steerQueue.set(route.sessionKey, pending);
|
|
363
|
+
log.log(`[BOT] Steer bypass: queued for agent_turn_prepare hook, sessionKey=${route.sessionKey}, queueDepth=${pending.length}, skipReg=${skipReg}`);
|
|
364
|
+
// Only decrement refCount when registerTaskId was called (non-skipReg).
|
|
365
|
+
// skipReg callers (CSPL/self-evolution) don't increment refCount.
|
|
366
|
+
if (!skipReg) {
|
|
367
|
+
decrementTaskIdRef(parsed.sessionId);
|
|
368
|
+
}
|
|
369
|
+
// Release the global dispatch init gate — steer doesn't need it.
|
|
370
|
+
params.onInitComplete?.();
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
342
373
|
// 🔑 For steer messages, pre-set steered=true so the dispatcher skips final
|
|
343
374
|
// response and cleanup — the first message's dispatcher handles those.
|
|
344
375
|
const steerState = { steered: isUpdate };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getSteerQueue(): Map<string, string[]>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Shared steer injection queue — bot.ts writes, index.ts agent_turn_prepare hook reads.
|
|
2
|
+
// Uses globalThis to survive module deduplication (same pattern as dispatcherUpdaters).
|
|
3
|
+
const _g = globalThis;
|
|
4
|
+
if (!_g.__xySteerInjectionQueue) {
|
|
5
|
+
_g.__xySteerInjectionQueue = new Map();
|
|
6
|
+
}
|
|
7
|
+
export function getSteerQueue() {
|
|
8
|
+
return _g.__xySteerInjectionQueue;
|
|
9
|
+
}
|