import { buildPagePublishFailureText } from '../prompts/page-generate.mjs'; import { selectSendableHtmlArtifacts, verifyPageArtifactContent } from '../verify/page-artifact.mjs'; import { repairArtifactSharePreview } from '../verify/share-preview-repair.mjs'; import { filterSharePreviewReadyArtifacts, verifyArtifactSharePreview } from '../verify/share-preview.mjs'; export function evaluatePageGenerateSendableArtifacts({ verifiedArtifacts = [], confirmedArtifacts = [], repairContext = {}, } = {}) { const publishDir = String(repairContext.publishDir ?? '').trim(); const previewOptions = { publishDir }; const sendable = selectSendableHtmlArtifacts({ verifiedArtifacts, confirmedArtifacts, publishDir, }); const verified = sendable.filter((artifact) => verifyPageArtifactContent(artifact, previewOptions).ok, ); const candidates = verified.length > 0 ? verified : sendable; const ready = filterSharePreviewReadyArtifacts(candidates, previewOptions); if (ready.length > 0) return ready; const repaired = []; for (const artifact of candidates) { if (!verifyPageArtifactContent(artifact, previewOptions).ok) continue; const result = repairArtifactSharePreview(artifact, repairContext); if (result.ok && verifyArtifactSharePreview(artifact, previewOptions).ok) { repaired.push(artifact); } } return repaired; } /** * Decide whether a page.generate reply may be delivered to the user. * Returns { action: 'send'|'fail'|'session_retry', failureText?, reason? } */ export function resolvePageGenerateOutcome({ reply, confirmedArtifacts = [], verifiedArtifacts = [], suspiciousPublishClaim = false, bareCompletionReply = false, htmlGenerationNeedsRetry = false, replyHasPublicLinks = false, topic = '', repairContext = {}, }) { const context = { topic: String(topic || repairContext.topic || '').trim(), publishDir: String(repairContext.publishDir ?? '').trim(), ...repairContext, }; const previewOptions = { publishDir: context.publishDir }; const sendable = evaluatePageGenerateSendableArtifacts({ verifiedArtifacts, confirmedArtifacts, repairContext: context, }); if (sendable.length > 0) { return { action: 'send', artifacts: sendable }; } if ( suspiciousPublishClaim || bareCompletionReply || (htmlGenerationNeedsRetry && replyHasPublicLinks && confirmedArtifacts.length === 0) ) { return { action: 'session_retry', reason: 'poisoned_or_fake_claim' }; } if (htmlGenerationNeedsRetry || suspiciousPublishClaim) { return { action: 'fail', failureText: buildPagePublishFailureText(), reason: 'skill_or_stub', }; } const previewCandidates = filterSharePreviewReadyArtifacts( selectSendableHtmlArtifacts({ verifiedArtifacts, confirmedArtifacts, publishDir: context.publishDir, }), previewOptions, ); if (previewCandidates.length === 0 && confirmedArtifacts.length > 0) { const previewIssue = verifyArtifactSharePreview( selectSendableHtmlArtifacts({ verifiedArtifacts, confirmedArtifacts, publishDir: context.publishDir, })[0] ?? confirmedArtifacts[0], previewOptions, ); return { action: 'fail', failureText: buildPagePublishFailureText({ missingPlatformBrand: previewIssue.reason === 'missing_platform_brand', missingSharePreview: previewIssue.reason !== 'missing_platform_brand', }), reason: previewIssue.reason ?? 'missing_share_preview', }; } if (!String(reply?.text ?? '').trim() && sendable.length === 0) { return { action: 'fail', failureText: buildPagePublishFailureText(), reason: 'empty_reply', }; } // Fail closed: page.generate must deliver a verified public HTML artifact. // Text-only planning replies ("我先搜索…") must not mark WeChat delivery done. return { action: 'fail', failureText: buildPagePublishFailureText(), reason: 'missing_page_artifact', }; }