fix: guard WeChat Page Data delivery with Aider review
Memind CI / Test, build, and release guards (push) Has been cancelled

This commit is contained in:
john
2026-07-28 00:26:24 +08:00
parent bc20e93893
commit 8bfb7959fc
13 changed files with 996 additions and 7 deletions
+81 -4
View File
@@ -27,7 +27,10 @@ import {
resolveWechatSessionAction,
} from './wechat/intent/session-action.mjs';
import { isPageGenerateText, isTopicResetText } from './wechat/intent/patterns.mjs';
import { isPageDataIntent } from './chat-skills.mjs';
import {
isPageDataDevIntent,
isPageDataIntent,
} from './chat-skills.mjs';
import { resolvePageGenerateOutcome } from './wechat/handlers/page-generate.mjs';
import {
buildWechatImageRunMetadata,
@@ -833,7 +836,7 @@ async function resolveHtmlPublishArtifacts({
looksLikeHtmlGenerationIntent(
intent?.agentText,
) ||
isPageDataIntent(intent?.agentText) ||
isWechatPageDataTask(intent?.agentText) ||
hasAnyPublicHtmlLink(reply?.text) ||
extractHtmlWriteTargets(
replyRequestMessages(reply),
@@ -970,6 +973,10 @@ export function shouldForceNewWechatAgentSession(wechatIntent, resetCandidate) {
);
}
export function isWechatPageDataTask(text) {
return isPageDataIntent(text) || isPageDataDevIntent(text);
}
export function isRecoverableWechatAgentSessionError(message) {
const normalized = String(message ?? '').trim();
if (!normalized) return false;
@@ -1457,6 +1464,7 @@ export function createWechatMpService({
sessionIntentClassifier = null,
htmlDeliveryAuthority = null,
pageDataFinishGuard = null,
pageDataDeliveryReviewer = null,
wechatFetch = undiciFetch,
linkExists = defaultPublicHtmlLinkExists,
logger = console,
@@ -1489,6 +1497,12 @@ export function createWechatMpService({
reliabilityGrayUsers: Array.isArray(config.reliabilityGrayUsers)
? config.reliabilityGrayUsers
: [],
pageDataAiderReviewEnabled:
config.pageDataAiderReviewEnabled === true,
pageDataAiderReviewUsers:
Array.isArray(config.pageDataAiderReviewUsers)
? config.pageDataAiderReviewUsers
: [],
agentReplyTimeoutMs: Math.max(
0,
Number(config.agentReplyTimeoutMs ?? DEFAULT_WECHAT_AGENT_REPLY_TIMEOUT_MS),
@@ -2268,6 +2282,46 @@ export function createWechatMpService({
}
};
const reviewWechatPageDataArtifacts = async ({
user,
requestId,
sessionId,
sourceMessageId,
originalUserText,
requestStartedAt,
artifacts,
forcePageData,
}) => {
const enabledForUser =
config.pageDataAiderReviewEnabled &&
isWechatMediaGrayUser(user, config.pageDataAiderReviewUsers);
if (!enabledForUser) return { action: 'skip', reason: 'disabled' };
if (typeof pageDataDeliveryReviewer?.reviewIfNeeded !== 'function') {
const error = new Error('微信 Page Data 强制 Aider 审核服务不可用');
error.code = 'PAGE_DATA_REVIEW_UNAVAILABLE';
error.retryable = false;
throw error;
}
const relativePaths = [
...new Set(
(Array.isArray(artifacts) ? artifacts : [])
.map((artifact) => String(artifact?.relativePath ?? '').trim())
.filter(Boolean),
),
];
return pageDataDeliveryReviewer.reviewIfNeeded({
sourceChannel: 'wechat_mp',
userId: user.userId,
requestId,
sessionId,
sourceMessageId,
originalUserText,
relativePaths,
requestStartedAt,
forcePageData,
});
};
const runIntentMessage = async ({ inbound, intent, user }) => {
const wechatIntent = await resolveWechatIntent(intent);
const mediaAnalysisEnabled = isWechatMediaGrayUser(user, config.mediaAnalysisGrayUsers);
@@ -2283,7 +2337,7 @@ export function createWechatMpService({
// Page Data delivery owns persistent files, datasets and two publication
// policies. Reusing a conversational route here can make a new request
// inspect/retry unrelated historical pages from that session.
const isPageDataRequest = isPageDataIntent(resetCandidate);
const isPageDataRequest = isWechatPageDataTask(resetCandidate);
const htmlArtifactDeliveryExpected = shouldDeliverWechatHtmlArtifacts(wechatIntent, intent);
const forceNew = shouldForceNewWechatAgentSession(wechatIntent, resetCandidate);
let route = await ensureWechatAgentSession({
@@ -2450,6 +2504,16 @@ export function createWechatMpService({
} else if (htmlGenerationNeedsRetry || suspiciousPublishClaim) {
throw new Error('stale_session_poisoned_completion');
}
await reviewWechatPageDataArtifacts({
user,
requestId,
sessionId,
sourceMessageId: intent.msgId,
originalUserText: resetCandidate,
requestStartedAt,
artifacts: [...confirmedArtifacts, ...publishArtifacts],
forcePageData: isPageDataRequest,
});
if (wechatIntent.kind === 'page.generate') {
await enforceFreshPageThumbnailDelivery({
artifacts: publishArtifacts,
@@ -2520,7 +2584,10 @@ export function createWechatMpService({
// another session while the finish guard is also active can turn one
// request into repairs against historical pages. Drop only this user's
// route so the next explicit request starts cleanly.
if (isPageDataRequest) {
if (
isPageDataRequest ||
String(err?.code ?? '').startsWith('PAGE_DATA_REVIEW_')
) {
await userAuth.clearWechatAgentRoute(config.appId, inbound.fromUserName).catch((clearErr) => {
logger.warn?.('WeChat MP page data route clear failed:', clearErr);
});
@@ -2678,6 +2745,16 @@ export function createWechatMpService({
} else if (htmlGenerationNeedsRetry || suspiciousPublishClaim) {
throw new Error(buildHtmlPublishFailureText());
}
await reviewWechatPageDataArtifacts({
user,
requestId: retryId,
sessionId,
sourceMessageId: intent.msgId,
originalUserText: resetCandidate,
requestStartedAt: retryStartedAt,
artifacts: [...confirmedArtifacts, ...publishArtifacts],
forcePageData: isPageDataRequest,
});
if (wechatIntent.kind === 'page.generate') {
await enforceFreshPageThumbnailDelivery({
artifacts: publishArtifacts,