@ynhcj/xiaoyi-channel 0.0.188-next → 0.0.189-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/src/bot.js +7 -3
- package/dist/src/outbound.js +38 -15
- package/dist/src/reply-dispatcher.js +5 -0
- package/package.json +1 -1
package/dist/src/bot.js
CHANGED
|
@@ -16,7 +16,7 @@ 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
18
|
import { registerTaskId, decrementTaskIdRef, hasActiveTask, } from "./task-manager.js";
|
|
19
|
-
import { registerSessionKeyMapping, getWaitState, markParentSettled, } from "./subagent-wait-state.js";
|
|
19
|
+
import { registerSessionKeyMapping, getWaitState, hasWaitState, markParentSettled, } from "./subagent-wait-state.js";
|
|
20
20
|
import { logger } from "./utils/logger.js";
|
|
21
21
|
/**
|
|
22
22
|
* Handle an incoming A2A message.
|
|
@@ -375,10 +375,14 @@ export async function handleXYMessage(params) {
|
|
|
375
375
|
const cleanup = () => {
|
|
376
376
|
if (cleaned)
|
|
377
377
|
return;
|
|
378
|
-
|
|
378
|
+
// Check for pending subagent wait on this session (any taskId).
|
|
379
|
+
// Steer dispatches have a different taskId, so we check both
|
|
380
|
+
// exact match and session-wide presence.
|
|
381
|
+
const pendingWait = getWaitState(parsed.sessionId, parsed.taskId) ??
|
|
382
|
+
(hasWaitState(parsed.sessionId) ? { deliveredCompletions: 0, expectedCompletions: 1 } : null);
|
|
379
383
|
if (pendingWait && pendingWait.deliveredCompletions < pendingWait.expectedCompletions) {
|
|
380
384
|
// Subagent wait active — skip cleanup, session stays alive
|
|
381
|
-
log.log(`[BOT] Cleanup suppressed — subagent wait active
|
|
385
|
+
log.log(`[BOT] Cleanup suppressed — subagent wait active on session, taskId=${parsed.taskId}`);
|
|
382
386
|
return;
|
|
383
387
|
}
|
|
384
388
|
cleaned = true;
|
package/dist/src/outbound.js
CHANGED
|
@@ -140,31 +140,49 @@ export const xyOutbound = {
|
|
|
140
140
|
// Resolve configuration
|
|
141
141
|
const config = resolveXYConfig(cfg);
|
|
142
142
|
// ── Subagent completion interception ─────────────────────────
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
143
|
+
// Detect subagent completions by checking if the resolved target
|
|
144
|
+
// matches a wait state's sessionId + taskId. This works regardless
|
|
145
|
+
// of ALS context because:
|
|
146
|
+
// - Normal agent turns: ALS enhances target with CURRENT taskId,
|
|
147
|
+
// which differs from wait state's taskId (no match → skip)
|
|
148
|
+
// - Subagent completions: ALS is null, resolveTarget enhanced
|
|
149
|
+
// from wait state's taskId (match → intercept)
|
|
150
|
+
// - Cron: to=DEFAULT_PUSH_MARKER (no match)
|
|
151
|
+
// - Message tool: ALS enhances with current taskId (no match
|
|
152
|
+
// unless the agent happens to be on the same yielded task)
|
|
153
|
+
if (to && to !== DEFAULT_PUSH_MARKER) {
|
|
149
154
|
const [targetSessionId, targetTaskId] = String(to).split("::");
|
|
150
155
|
const waitState = getWaitState(targetSessionId, targetTaskId);
|
|
151
|
-
|
|
156
|
+
const alsCtx = getCurrentSessionContext();
|
|
157
|
+
// Only intercept when ALS is null OR the target taskId matches
|
|
158
|
+
// the wait state (not the current ALS taskId). This ensures
|
|
159
|
+
// we don't intercept normal agent turn deliveries.
|
|
160
|
+
const isFromWaitState = waitState &&
|
|
161
|
+
(!alsCtx || alsCtx.taskId !== targetTaskId);
|
|
162
|
+
if (isFromWaitState && !waitState.finalizationClaimed) {
|
|
163
|
+
const log = logger.withContext(waitState.sessionId, waitState.taskId);
|
|
164
|
+
log.log(`[xyOutbound.sendText] Subagent completion detected, als=${!!alsCtx}, delivered=${waitState.deliveredCompletions}/${waitState.expectedCompletions}`);
|
|
152
165
|
const delivered = markCompletionDelivered(targetSessionId, targetTaskId, text);
|
|
153
166
|
if (delivered) {
|
|
154
167
|
const { state, isComplete, shouldFinalize } = delivered;
|
|
155
|
-
const log = logger.withContext(state.sessionId, state.taskId);
|
|
156
168
|
if (shouldFinalize) {
|
|
157
|
-
// All completions arrived and parent already settled →
|
|
158
|
-
log.log(`[xyOutbound.sendText]
|
|
169
|
+
// All completions arrived and parent already settled → finalize
|
|
170
|
+
log.log(`[xyOutbound.sendText] Finalizing subagent results`);
|
|
159
171
|
await deliverSubagentFinalResult({ config, state, reason: "all-subagent-results-delivered-after-parent-settled", text: text });
|
|
172
|
+
// Subagent completion delivered via A2A, skip push
|
|
173
|
+
return {
|
|
174
|
+
channel: "xiaoyi-channel",
|
|
175
|
+
messageId: state.artifactId,
|
|
176
|
+
chatId: to,
|
|
177
|
+
};
|
|
160
178
|
}
|
|
161
|
-
|
|
162
|
-
// All completions arrived but parent hasn't settled yet → defer
|
|
179
|
+
if (isComplete) {
|
|
180
|
+
// All completions arrived but parent hasn't settled yet → defer to onSettled
|
|
163
181
|
log.log(`[xyOutbound.sendText] All subagent results arrived before parent settled, deferring finalization`);
|
|
164
182
|
}
|
|
165
183
|
else {
|
|
166
|
-
// Still waiting for more completions → send progress
|
|
167
|
-
log.log(`[xyOutbound.sendText] Subagent
|
|
184
|
+
// Still waiting for more completions → send progress via A2A, skip push
|
|
185
|
+
log.log(`[xyOutbound.sendText] Subagent progress ${state.deliveredCompletions}/${state.expectedCompletions}`);
|
|
168
186
|
try {
|
|
169
187
|
await sendStatusUpdate({
|
|
170
188
|
config,
|
|
@@ -181,7 +199,12 @@ export const xyOutbound = {
|
|
|
181
199
|
}
|
|
182
200
|
}
|
|
183
201
|
}
|
|
184
|
-
//
|
|
202
|
+
// Skip push delivery for subagent completions — only send via A2A
|
|
203
|
+
return {
|
|
204
|
+
channel: "xiaoyi-channel",
|
|
205
|
+
messageId: waitState.artifactId,
|
|
206
|
+
chatId: to,
|
|
207
|
+
};
|
|
185
208
|
}
|
|
186
209
|
}
|
|
187
210
|
// ── End subagent interception ───────────────────────────────
|
|
@@ -463,7 +463,12 @@ export function createXYReplyDispatcher(params) {
|
|
|
463
463
|
else {
|
|
464
464
|
// 新文本不以已累积内容为前缀(如工具调用后模型重新开始生成),
|
|
465
465
|
// 更新 accumulatedText 为当前文本,后续基于此新前缀做去重
|
|
466
|
+
const wasFirstRound = accumulatedText.length === 0;
|
|
466
467
|
accumulatedText = "";
|
|
468
|
+
// 新一轮输出前加换行分隔(第一轮除外)
|
|
469
|
+
if (sendText.length > 0 && !wasFirstRound) {
|
|
470
|
+
sendText = "\n" + sendText;
|
|
471
|
+
}
|
|
467
472
|
}
|
|
468
473
|
accumulatedText += sendText;
|
|
469
474
|
hasSentResponse = true;
|