fix(wechat): detect tool_calls errors in Finish reply and sanitize outbound text
When Goose surfaces session poison errors as assistant text instead of Error events, assertWechatAgentReplyIsSendable now throws to trigger session recreation. Outbound sanitization prevents raw API errors from reaching WeChat users. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+64
-3
@@ -255,11 +255,13 @@ async function executeSessionReply(apiFetch, sessionId, requestId, prompt, metad
|
||||
if (!hasScopedAssistantUpdate || !assistant) {
|
||||
throw new Error('本轮未收到可发送的新回复,请稍后重试');
|
||||
}
|
||||
return {
|
||||
const reply = {
|
||||
text: messageVisibleText(assistant),
|
||||
tokenState: event.token_state ?? null,
|
||||
messages,
|
||||
};
|
||||
assertWechatAgentReplyIsSendable(reply);
|
||||
return reply;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -960,6 +962,65 @@ export function isRecoverableWechatAgentSessionError(message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function collectWechatAgentReplyVisibleTexts(reply) {
|
||||
const texts = [];
|
||||
const seen = new Set();
|
||||
const append = (text) => {
|
||||
const normalized = String(text ?? '').trim();
|
||||
if (!normalized || seen.has(normalized)) return;
|
||||
seen.add(normalized);
|
||||
texts.push(normalized);
|
||||
};
|
||||
append(reply?.text);
|
||||
for (const message of reply?.messages ?? []) {
|
||||
if (message?.role !== 'assistant') continue;
|
||||
append(messageVisibleText(message));
|
||||
}
|
||||
return texts;
|
||||
}
|
||||
|
||||
export function findRecoverableWechatAgentErrorInReply(reply) {
|
||||
for (const text of collectWechatAgentReplyVisibleTexts(reply)) {
|
||||
if (isRecoverableWechatAgentSessionError(text)) return text;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function assertWechatAgentReplyIsSendable(reply) {
|
||||
const recoverable = findRecoverableWechatAgentErrorInReply(reply);
|
||||
if (recoverable) throw new Error(recoverable);
|
||||
}
|
||||
|
||||
export function isWechatAgentApiErrorText(message) {
|
||||
const normalized = String(message ?? '').trim();
|
||||
if (!normalized) return false;
|
||||
if (isRecoverableWechatAgentSessionError(normalized)) return true;
|
||||
if (/Ran into this error:/i.test(normalized)) return true;
|
||||
return /Request failed:\s*(Bad request|Internal Server Error)/i.test(normalized);
|
||||
}
|
||||
|
||||
export function sanitizeWechatAgentOutboundText(text) {
|
||||
const normalized = String(text ?? '').trim();
|
||||
if (!normalized) return normalized;
|
||||
const stripped = normalized
|
||||
.replace(/Ran into this error:[\s\S]*?(?=\n\n|$)/gi, '')
|
||||
.replace(/Request failed:\s*Bad request \(400\):[\s\S]*?(?=\n\n|$)/gi, '')
|
||||
.replace(/\n{3,}/g, '\n\n')
|
||||
.trim();
|
||||
if (!stripped || isWechatAgentApiErrorText(stripped)) {
|
||||
return '刚才专属会话状态异常,我已切换到新会话。请再发一次你的需求。';
|
||||
}
|
||||
return stripped;
|
||||
}
|
||||
|
||||
function formatWechatAgentFailureMessage(err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
if (isRecoverableWechatAgentSessionError(message) || isWechatAgentApiErrorText(message)) {
|
||||
return '刚才专属会话状态异常,我已切换到新会话。请再发一次你的需求。';
|
||||
}
|
||||
return `这次转发到专属 Agent 失败了:${message.slice(0, 200)}`;
|
||||
}
|
||||
|
||||
function normalizeNumber(value) {
|
||||
if (value === '' || value === null || value === undefined) return null;
|
||||
const num = Number(value);
|
||||
@@ -1571,7 +1632,7 @@ export function createWechatMpService({
|
||||
user = null,
|
||||
{ verifiedHtmlUrls = [], linkExistsForRequest = linkExists } = {},
|
||||
) => {
|
||||
const formatted = formatWechatOutboundText(content, user);
|
||||
const formatted = formatWechatOutboundText(sanitizeWechatAgentOutboundText(content), user);
|
||||
const verifiedUrlSet = new Set(
|
||||
verifiedHtmlUrls
|
||||
.map((url) => String(url ?? '').trim())
|
||||
@@ -2504,7 +2565,7 @@ export function createWechatMpService({
|
||||
logger.error?.('WeChat MP background reply failed:', err);
|
||||
return sendCustomerServiceText(
|
||||
inbound.fromUserName,
|
||||
`这次转发到专属 Agent 失败了:${err instanceof Error ? err.message : String(err)}`,
|
||||
formatWechatAgentFailureMessage(err),
|
||||
boundUser,
|
||||
).catch(() => {});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user