opencode+dsh: 空回复兜底 —— idle 但无 assistant 文本时回失败通知
缺口:模型 idle(轮次正常结束)但没有产出任何 assistant 文本时, 两个插件此前静默 return —— 发件人等不到任何回复也得不到交代。 (模型「全部失败」已有 renderFailureReport 兜底,但「跑完却没产出」 不等于失败,走不到那条。) 补: - opencode relaySummary: if (!last) → 发一封「处理失败:无回复文本」通知 - dsh agent/status idle: if (!lastText) → 同款通知 - 都走 relay:summary 免配额 + relay_key 幂等 - 都只给人类来信发(Agent 间不自动转发)
This commit is contained in:
@ -1536,7 +1536,31 @@ export function apply(ctx: any, config: PluginConfig): void {
|
|||||||
// 取最后一条 assistant 消息的文本
|
// 取最后一条 assistant 消息的文本
|
||||||
const events = agent.session?.events ?? [];
|
const events = agent.session?.events ?? [];
|
||||||
const lastText = lastAssistantText(events);
|
const lastText = lastAssistantText(events);
|
||||||
if (!lastText) return;
|
if (!lastText) {
|
||||||
|
// 模型 idle 但没有任何 assistant 文本:空回复/被中断在文本前。
|
||||||
|
// 此前静默返回 —— 发件人等不到任何回复(缺的是 B-6 的「模型全部失败」
|
||||||
|
// 同款兜底,只是这次没有失败可报却也没有产出)。给发件人一封失败通知。
|
||||||
|
// 只给人类来信发:Agent 之间不自动转发(与上面同一判据)。
|
||||||
|
try {
|
||||||
|
await client.post('/mail/send', {
|
||||||
|
to: mctx.replyTo,
|
||||||
|
subject: `处理失败: ${mctx.subject || '(无主题)'}`,
|
||||||
|
body: [
|
||||||
|
`这封邮件(主题:${mctx.subject || '(无主题)'})的处理轮次已结束,但没有产出任何回复文本。`,
|
||||||
|
'',
|
||||||
|
'可能原因:模型空响应、输出被中断、或工具调用异常耗尽轮次。',
|
||||||
|
'请检查后重试。',
|
||||||
|
].join('\n'),
|
||||||
|
reply_to: mctx.mailID || '',
|
||||||
|
relay: 'summary',
|
||||||
|
relay_key: clampRelayKey(`empty-reply:${mailSessionID}`),
|
||||||
|
});
|
||||||
|
console.error(`[dsh-mail-bridge] 已发空回复失败通知给 ${mctx.replyTo}`);
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error(`[dsh-mail-bridge] 空回复失败通知也发不出去: ${e?.message || e}`);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 本轮模型已亲手回过这条线索 → 不再自动转发(与 opencode 侧同一取舍)
|
// 本轮模型已亲手回过这条线索 → 不再自动转发(与 opencode 侧同一取舍)
|
||||||
if (shouldSkipAutoRelay(explicitSends.get(String(agent.id)), mctx.replyTo, mctx.mailID)) {
|
if (shouldSkipAutoRelay(explicitSends.get(String(agent.id)), mctx.replyTo, mctx.mailID)) {
|
||||||
|
|||||||
@ -762,7 +762,32 @@ async function relaySummary(client, directory, sessionID) {
|
|||||||
if (text) { last = { id: m.info.id, text }; }
|
if (text) { last = { id: m.info.id, text }; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!last) return null;
|
if (!last) {
|
||||||
|
// 模型 idle 但没有任何完成的 assistant 文本:空回复或被中断在文本前。
|
||||||
|
// 此前静默返回 —— 发件人等不到任何回复(B-6 的缺口:跳过了
|
||||||
|
// 「模型全部失败」那条兜底,因为没有失败可报,却也没有产出)。
|
||||||
|
// 给发件人一封失败通知,让他知道这封已经处理过了但没结果。
|
||||||
|
try {
|
||||||
|
await apiPost("/mail/send", {
|
||||||
|
to: ctx?.replyTo || "",
|
||||||
|
subject: `处理失败: ${ctx?.subject || "(无主题)"}`,
|
||||||
|
body: [
|
||||||
|
`这封邮件(主题:${ctx?.subject || "(无主题)"})的处理轮次已结束,但没有产出任何回复文本。`,
|
||||||
|
"",
|
||||||
|
"可能原因:模型空响应、输出被中断、或工具调用异常耗尽轮次。",
|
||||||
|
"请检查后重试。",
|
||||||
|
].join("\n"),
|
||||||
|
reply_to: ctx?.mailID || "",
|
||||||
|
relay: "summary",
|
||||||
|
relay_key: clampRelayKey(`empty-reply:${mailSessionID || sessionID}`),
|
||||||
|
});
|
||||||
|
console.error(`[mail-bridge] 已发空回复失败通知给 ${ctx?.replyTo || ""}`);
|
||||||
|
} catch (e) {
|
||||||
|
// 失败通知也发不出去就静默:这次至少试过了
|
||||||
|
console.error("[mail-bridge] 空回复失败通知也发不出去:", e?.message || e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// 本地去重(服务端另有 relay_key 幂等兜底,这里只是少打一次网关)
|
// 本地去重(服务端另有 relay_key 幂等兜底,这里只是少打一次网关)
|
||||||
if (relayedSummaries.get(sessionID) === last.id) return null;
|
if (relayedSummaries.get(sessionID) === last.id) return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user