@ynhcj/xiaoyi-channel 0.0.225-next → 0.0.227-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
|
@@ -406,9 +406,10 @@ function registerSubagentHooks(api) {
|
|
|
406
406
|
if (transition?.shouldFinalize) {
|
|
407
407
|
logger.log(`[XY-SUBAGENT-END] Starting finalization...`);
|
|
408
408
|
// Grace: announce 投递的 sendText 与 subagent_ended hook 并发到达
|
|
409
|
-
// (实测 sendText 可能先于 hook 约 1s
|
|
410
|
-
//
|
|
411
|
-
|
|
409
|
+
// (实测 sendText 可能先于 hook 约 1s,也可能晚于 hook;长文本还会
|
|
410
|
+
// 被 textChunkLimit 分片成多次 sendText)。捕获窗口直到
|
|
411
|
+
// finalDeliveryStarted 才关闭,此处的等待直接扩大迟到来片的入窗机会。
|
|
412
|
+
await new Promise((r) => setTimeout(r, 3000));
|
|
412
413
|
// 最终交付由对话管理层统一负责(含 config 解析、final 帧、清理)
|
|
413
414
|
await deliverSubagentFinalResult({
|
|
414
415
|
state: transition.state,
|
|
@@ -261,6 +261,7 @@ export function markSubagentSpawned(sessionKey) {
|
|
|
261
261
|
completionTexts: [],
|
|
262
262
|
parentSettled: false,
|
|
263
263
|
finalizationClaimed: false,
|
|
264
|
+
finalDeliveryStarted: false,
|
|
264
265
|
};
|
|
265
266
|
states.push(state);
|
|
266
267
|
}
|
|
@@ -367,6 +368,9 @@ export async function deliverSubagentFinalResult(params) {
|
|
|
367
368
|
log.error(`[SUBAGENT-FINAL] No cached XY config, cannot deliver final result`);
|
|
368
369
|
return;
|
|
369
370
|
}
|
|
371
|
+
// 关闭捕获窗口:此后到达的 sendText 不再并入 final 文本。
|
|
372
|
+
// 等待态在发送后立即清除,迟到的 announce 文本回退 push 通道兜底(不丢消息)。
|
|
373
|
+
state.finalDeliveryStarted = true;
|
|
370
374
|
const finalText = state.completionTexts.length > 0
|
|
371
375
|
? state.completionTexts.join("\n\n")
|
|
372
376
|
: (params.text || undefined) ?? "子任务已完成";
|
|
@@ -16,6 +16,12 @@ export interface SubagentWaitState {
|
|
|
16
16
|
completionTexts: string[];
|
|
17
17
|
parentSettled: boolean;
|
|
18
18
|
finalizationClaimed: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* final 帧开始交付(deliverSubagentFinalResult 入口)后置位。
|
|
21
|
+
* 捕获窗口以此为准:grace 期间迟到的 announce 文本仍可并入 final;
|
|
22
|
+
* 置位后到达的 sendText 不再捕获(等待态随即清除,回退 push 兜底)。
|
|
23
|
+
*/
|
|
24
|
+
finalDeliveryStarted: boolean;
|
|
19
25
|
}
|
|
20
26
|
export interface SubagentWaitTransition {
|
|
21
27
|
state: SubagentWaitState;
|
|
@@ -115,16 +115,18 @@ export const xyOutbound = {
|
|
|
115
115
|
chatId: typeof to === "string" ? to : "",
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
|
-
// ── Subagent completion text capture
|
|
118
|
+
// ── Subagent completion text capture + push 抑制 ─────────────
|
|
119
119
|
// Subagent completions may arrive here when openclaw's announce
|
|
120
120
|
// flow delivers through the channel outbound. We capture the text
|
|
121
121
|
// for the final A2A response but do NOT track delivery count here.
|
|
122
122
|
// Delivery tracking is done by the subagent_ended hook (index.ts).
|
|
123
123
|
//
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
124
|
+
// 正常对话的 subagent announce 一律不发 push:结果由保活机制经
|
|
125
|
+
// A2A final 帧交付,push webhook 通知冗余。返回合成成功(与 cron
|
|
126
|
+
// 吞没路径同理),保证 openclaw durable-delivery 记账正常,避免
|
|
127
|
+
// 触发 direct-primary fallback 重试。
|
|
128
|
+
// cron 的 announce 不受此分支影响:cron sendText 无 target
|
|
129
|
+
// (DEFAULT_PUSH_MARKER),且 cron 会话不存在 wait state,push 照常。
|
|
128
130
|
if (to && to !== DEFAULT_PUSH_MARKER) {
|
|
129
131
|
const [targetSessionId, targetTaskId] = String(to).split("::");
|
|
130
132
|
const waitState = getWaitState(targetSessionId, targetTaskId);
|
|
@@ -132,15 +134,30 @@ export const xyOutbound = {
|
|
|
132
134
|
// 同样携带父会话 ALS 上下文(resolveTarget 据此增强 target),ALS guard
|
|
133
135
|
// 会把 announce 误判为父 turn 自己的 sendText 而漏捕获。
|
|
134
136
|
// 改用 parentSettled 区分:父 run 存活期间的 sendText 是父自己的 message
|
|
135
|
-
//
|
|
136
|
-
// subagent 完成结果的 announce
|
|
137
|
+
// 工具调用(不捕获、push 照常);父 settle 后命中 waitState 的 sendText
|
|
138
|
+
// 即为 subagent 完成结果的 announce 投递(捕获 + 吞掉 push)。
|
|
139
|
+
// 捕获窗口以 finalDeliveryStarted 为准(而非 finalizationClaimed):
|
|
140
|
+
// finalizationClaimed 在 markSubagentEnded 时就置位,若 announce 的
|
|
141
|
+
// sendText 晚于 subagent_ended hook 到达(如长文本被 textChunkLimit
|
|
142
|
+
// 分片、投递时序竞争),claim 会提前关闭捕获导致内容丢失——配合
|
|
143
|
+
// push 抑制,丢失是永久的。finalDeliveryStarted 在 grace 结束后、
|
|
144
|
+
// final 帧发送前才置位,grace 期间迟到的文本仍能并入 final。
|
|
137
145
|
const isFromWaitState = waitState && waitState.parentSettled;
|
|
138
|
-
if (isFromWaitState
|
|
146
|
+
if (isFromWaitState) {
|
|
139
147
|
const log = logger.withContext(waitState.sessionId, waitState.taskId);
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
148
|
+
if (!waitState.finalDeliveryStarted) {
|
|
149
|
+
log.log(`[xyOutbound.sendText] Subagent completion text captured, len=${text?.length ?? 0}`);
|
|
150
|
+
addCompletionText(targetSessionId, targetTaskId, text);
|
|
151
|
+
log.log(`[xyOutbound.sendText] Subagent announce — swallowing push (result delivered via A2A final frame)`);
|
|
152
|
+
return {
|
|
153
|
+
channel: "xiaoyi-channel",
|
|
154
|
+
messageId: `subagent-swallowed-${Date.now()}`,
|
|
155
|
+
chatId: typeof to === "string" ? to : "",
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
// final 帧已开始交付:不再捕获也不再吞没,fall through 走 push 兜底,
|
|
159
|
+
// 避免迟到的 announce 文本被静默丢弃。
|
|
160
|
+
log.log(`[xyOutbound.sendText] Subagent announce arrived after final delivery started — falling back to push`);
|
|
144
161
|
}
|
|
145
162
|
}
|
|
146
163
|
// ── End subagent text capture ───────────────────────────────
|