@ynhcj/xiaoyi-channel 0.0.130-next → 0.0.131-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
CHANGED
|
@@ -331,6 +331,21 @@ export async function handleXYMessage(params) {
|
|
|
331
331
|
return dispatchPromise;
|
|
332
332
|
},
|
|
333
333
|
});
|
|
334
|
+
// 🔑 Steer 重试:如果首次注入失败(run 还没 streaming),等待后重试
|
|
335
|
+
if (isUpdate) {
|
|
336
|
+
await retrySteerIfNeeded({
|
|
337
|
+
steerState,
|
|
338
|
+
sessionId: parsed.sessionId,
|
|
339
|
+
sessionKey: route.sessionKey,
|
|
340
|
+
steerText: textForAgent,
|
|
341
|
+
ctxPayload,
|
|
342
|
+
cfg,
|
|
343
|
+
runtime,
|
|
344
|
+
parsed,
|
|
345
|
+
route,
|
|
346
|
+
deviceType,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
334
349
|
logger.log(`[BOT] ✅ Dispatcher completed for session: ${parsed.sessionId}`);
|
|
335
350
|
logger.log(`xy: dispatch complete (session=${parsed.sessionId})`);
|
|
336
351
|
}
|
|
@@ -386,3 +401,114 @@ function buildXYMediaPayload(mediaList) {
|
|
|
386
401
|
MediaTypes: mediaTypes.length > 0 ? mediaTypes : undefined,
|
|
387
402
|
};
|
|
388
403
|
}
|
|
404
|
+
const STEER_RETRY_DELAYS = [2000, 3000, 5000];
|
|
405
|
+
/**
|
|
406
|
+
* Steer 注入重试:当首次注入因 run 未 streaming 而失败时,
|
|
407
|
+
* 等待递增延迟后重试,最多尝试 3 次。
|
|
408
|
+
*/
|
|
409
|
+
async function retrySteerIfNeeded(params) {
|
|
410
|
+
const { steerState, sessionId, steerText } = params;
|
|
411
|
+
// 首次成功则无需重试
|
|
412
|
+
if (steerState.steerResult === 'success') {
|
|
413
|
+
logger.log(`[STEER-RETRY] ✅ Steer succeeded on first attempt`);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
// 只有明确失败才重试;无结果可能是异常路径,也尝试重试
|
|
417
|
+
if (steerState.steerResult !== 'fail' && steerState.steerResult !== undefined) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
logger.log(`[STEER-RETRY] ⚠️ First steer attempt result=${steerState.steerResult}, starting retry loop`);
|
|
421
|
+
const core = getXYRuntime();
|
|
422
|
+
for (let attempt = 0; attempt < STEER_RETRY_DELAYS.length; attempt++) {
|
|
423
|
+
const delay = STEER_RETRY_DELAYS[attempt];
|
|
424
|
+
logger.log(`[STEER-RETRY] ⏳ Waiting ${delay}ms before retry #${attempt + 1}`);
|
|
425
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
426
|
+
// 第一条消息已结束,无需继续
|
|
427
|
+
if (!hasActiveTask(sessionId)) {
|
|
428
|
+
logger.log(`[STEER-RETRY] ℹ️ First message completed, skip retry`);
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
logger.log(`[STEER-RETRY] 🔄 Retrying steer dispatch #${attempt + 1}`);
|
|
432
|
+
// 构建新的 steer 消息上下文
|
|
433
|
+
const speaker = sessionId;
|
|
434
|
+
const messageBody = `${speaker}: ${steerText}`;
|
|
435
|
+
const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(params.cfg);
|
|
436
|
+
const body = core.channel.reply.formatAgentEnvelope({
|
|
437
|
+
channel: "xiaoyi-channel",
|
|
438
|
+
from: speaker,
|
|
439
|
+
timestamp: new Date(),
|
|
440
|
+
envelope: envelopeOptions,
|
|
441
|
+
body: messageBody,
|
|
442
|
+
});
|
|
443
|
+
const retryCtx = core.channel.reply.finalizeInboundContext({
|
|
444
|
+
Body: body,
|
|
445
|
+
RawBody: steerText,
|
|
446
|
+
CommandBody: steerText,
|
|
447
|
+
From: sessionId,
|
|
448
|
+
To: sessionId,
|
|
449
|
+
SessionKey: params.route.sessionKey,
|
|
450
|
+
AccountId: params.route.accountId,
|
|
451
|
+
ChatType: "direct",
|
|
452
|
+
GroupSubject: undefined,
|
|
453
|
+
SenderName: sessionId,
|
|
454
|
+
SenderId: sessionId,
|
|
455
|
+
Provider: "xiaoyi-channel",
|
|
456
|
+
Surface: "xiaoyi-channel",
|
|
457
|
+
MessageSid: `${params.parsed.taskId}_${params.deviceType}`,
|
|
458
|
+
Timestamp: Date.now(),
|
|
459
|
+
WasMentioned: false,
|
|
460
|
+
CommandAuthorized: true,
|
|
461
|
+
OriginatingChannel: "xiaoyi-channel",
|
|
462
|
+
OriginatingTo: sessionId,
|
|
463
|
+
ReplyToBody: undefined,
|
|
464
|
+
});
|
|
465
|
+
const retryState = { steered: true };
|
|
466
|
+
const { dispatcher: retryDispatcher, replyOptions: retryReplyOptions } = createXYReplyDispatcher({
|
|
467
|
+
cfg: params.cfg,
|
|
468
|
+
runtime: params.runtime,
|
|
469
|
+
sessionId,
|
|
470
|
+
taskId: params.parsed.taskId,
|
|
471
|
+
messageId: params.parsed.messageId,
|
|
472
|
+
accountId: params.route.accountId,
|
|
473
|
+
steerState: retryState,
|
|
474
|
+
});
|
|
475
|
+
const sessionContext = {
|
|
476
|
+
config: resolveXYConfig(params.cfg),
|
|
477
|
+
sessionId,
|
|
478
|
+
taskId: params.parsed.taskId,
|
|
479
|
+
messageId: params.parsed.messageId,
|
|
480
|
+
agentId: params.route.accountId,
|
|
481
|
+
deviceType: params.deviceType,
|
|
482
|
+
};
|
|
483
|
+
try {
|
|
484
|
+
await core.channel.reply.withReplyDispatcher({
|
|
485
|
+
dispatcher: retryDispatcher,
|
|
486
|
+
onSettled: () => {
|
|
487
|
+
logger.log(`[STEER-RETRY] 🏁 Retry dispatch settled, result=${retryState.steerResult}`);
|
|
488
|
+
},
|
|
489
|
+
run: () => {
|
|
490
|
+
return runWithSessionContext(sessionContext, async () => {
|
|
491
|
+
const result = await core.channel.reply.dispatchReplyFromConfig({
|
|
492
|
+
ctx: retryCtx,
|
|
493
|
+
cfg: params.cfg,
|
|
494
|
+
dispatcher: retryDispatcher,
|
|
495
|
+
replyOptions: retryReplyOptions,
|
|
496
|
+
});
|
|
497
|
+
logger.log(`[STEER-RETRY] dispatch result: ${JSON.stringify(result)}`);
|
|
498
|
+
return result;
|
|
499
|
+
});
|
|
500
|
+
},
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
catch (err) {
|
|
504
|
+
logger.error(`[STEER-RETRY] ❌ Retry dispatch #${attempt + 1} threw: ${String(err)}`);
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
if (retryState.steerResult === 'success') {
|
|
508
|
+
logger.log(`[STEER-RETRY] ✅ Steer succeeded on retry #${attempt + 1}`);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
logger.log(`[STEER-RETRY] ⚠️ Retry #${attempt + 1} result=${retryState.steerResult}, continuing`);
|
|
512
|
+
}
|
|
513
|
+
logger.warn(`[STEER-RETRY] ❌ All retries exhausted for session ${sessionId}`);
|
|
514
|
+
}
|
|
@@ -115,7 +115,14 @@ export function createXYReplyDispatcher(params) {
|
|
|
115
115
|
deliver: async (payload, info) => {
|
|
116
116
|
// 🔑 steered dispatch不发送内容(让主dispatcher处理)
|
|
117
117
|
if (steerState.steered) {
|
|
118
|
-
|
|
118
|
+
const text = payload.text ?? '';
|
|
119
|
+
if (text.includes('steered current session')) {
|
|
120
|
+
steerState.steerResult = 'success';
|
|
121
|
+
}
|
|
122
|
+
else if (text.includes('not accepting steering') || text.includes('No active run')) {
|
|
123
|
+
steerState.steerResult = 'fail';
|
|
124
|
+
}
|
|
125
|
+
logger.log(`[DELIVER] Steered dispatch - result=${steerState.steerResult}, info.kind=${info?.kind}, text=${text.slice(0, 80)}`);
|
|
119
126
|
return;
|
|
120
127
|
}
|
|
121
128
|
const text = payload.text ?? "";
|