type ScanReport = Record; type FeeEstimate = Record; type MissionPayload = Record; function cleanText(value: unknown, fallback = ""): string { if (typeof value !== "string") return fallback; const trimmed = value.trim(); return trimmed || fallback; } function asNumber(value: unknown): number | null { return typeof value === "number" && Number.isFinite(value) ? value : null; } function formatMoney(value: unknown): string { const numeric = asNumber(value); if (numeric == null) return "--"; return `$${numeric.toFixed(2)}`; } export function buildScanCopilot(input: { domain: string; url?: string; pageText?: string; darkPatternReport: ScanReport; trustRating?: { score?: number; tier?: string } | null; }) { const report = input.darkPatternReport ?? {}; const detected: Array<{ label: string; explanation: string }> = []; if (report.falseUrgency?.detected) { detected.push({ label: "False urgency", explanation: cleanText(report.falseUrgency?.evidence, "The page uses time pressure to rush the decision."), }); } if (report.falseScarcity?.detected) { detected.push({ label: "False scarcity", explanation: cleanText(report.falseScarcity?.evidence, "The page suggests stock pressure or crowd pressure."), }); } if (report.confirmShaming?.detected) { detected.push({ label: "Confirm shaming", explanation: cleanText(report.confirmShaming?.shamingText, "The decline path uses guilt-inducing copy."), }); } if (report.hiddenFees?.detected) { const fees = Array.isArray(report.hiddenFees?.feeItems) ? report.hiddenFees.feeItems.map((fee: any) => `${fee.label} ${formatMoney(fee.amount)}`).join(", ") : ""; detected.push({ label: "Hidden fees", explanation: fees || "The page appears to hold back mandatory price information until later in checkout.", }); } if (report.preCheckedAddOns?.detected) { detected.push({ label: "Pre-checked add-ons", explanation: cleanText( Array.isArray(report.preCheckedAddOns?.addOnLabels) ? report.preCheckedAddOns.addOnLabels.join(", ") : "", "Optional extras are already selected for the shopper.", ), }); } if (report.misdirection?.detected) { detected.push({ label: "Misdirection", explanation: cleanText(report.misdirection?.hiddenDeclineText, "The safer or cheaper path is visually deprioritized."), }); } const trustScore = report.trustScore ?? input.trustRating?.score ?? 50; const headline = detected.length === 0 ? `Guardian thinks ${input.domain} looks relatively clean from the submitted checkout content.` : `Guardian thinks ${input.domain} is using ${detected.length} manipulation signal${detected.length === 1 ? "" : "s"}.`; const recommendation = detected.length === 0 ? "Proceed, but keep Guardian active through the final confirmation step." : detected.length >= 3 ? "Slow down, verify the total, and look for a cleaner alternative before buying." : "Proceed cautiously and remove optional extras before continuing."; return { mode: "scan", headline, confidence: detected.length === 0 ? "medium" : detected.length >= 3 ? "high" : "medium", inputSummary: `Input reviewed for ${input.domain}${input.url ? ` at ${input.url}` : ""}.`, reasoning: detected.length ? detected.map((item) => `${item.label}: ${item.explanation}`) : ["No major dark-pattern signature was triggered in the submitted checkout content."], recommendation, trustScore, followUps: detected.length === 0 ? ["Keep watching for last-second fees at the payment step."] : [ "Check whether the final payable amount matches the first quoted price.", "Look for a neutral decline path and remove any default-on add-ons.", ], }; } export function buildFeeCopilot(input: { domain: string; merchantType: string; listedPrice: number; result: FeeEstimate; }) { const estimatedTotal = asNumber(input.result?.estimatedTotal) ?? input.listedPrice; const savingsOpportunity = asNumber(input.result?.savingsOpportunity) ?? Math.max(0, estimatedTotal - input.listedPrice); const breakdown = Array.isArray(input.result?.feeBreakdown) ? input.result.feeBreakdown.map((fee: any) => `${fee.label} ${formatMoney(fee.amount)}`) : []; return { mode: "fee_estimate", headline: `Guardian thinks the advertised ${formatMoney(input.listedPrice)} on ${input.domain} is likely to end closer to ${formatMoney(estimatedTotal)}.`, confidence: cleanText(input.result?.confidence, "medium"), inputSummary: `Input reviewed for ${input.domain} in the ${input.merchantType} category.`, reasoning: [ breakdown.length ? `Likely hidden cost layers: ${breakdown.join(", ")}.` : "Guardian expects taxes, surcharges, or service fees beyond the headline price.", `Modeled uplift over headline price: ${formatMoney(savingsOpportunity)}.`, ], recommendation: savingsOpportunity <= 0 ? "The listed price looks close to the real total." : savingsOpportunity / Math.max(input.listedPrice, 1) > 0.2 ? "Treat this as a high-risk price gap and compare alternatives before checkout." : "Proceed with caution and verify the fee breakdown before paying.", trustScore: null, followUps: [ "Try a full mission run on the merchant to verify the true payable amount.", "Look for the same product or booking on a cleaner alternative site.", ], }; } export function buildMissionCopilot(input: { domain: string; purchaseGoal: string; mission: MissionPayload; }) { const recommendation = cleanText(input.mission?.recommendation, "proceed"); const trustScore = asNumber(input.mission?.trust?.score); const signals = Array.isArray(input.mission?.manipulativeSignals) ? input.mission.manipulativeSignals : []; const alternatives = Array.isArray(input.mission?.alternatives) ? input.mission.alternatives : []; const dealItems = Array.isArray(input.mission?.dealIntelligence?.items) ? input.mission.dealIntelligence.items : []; const misleadingDeals = dealItems.filter((item: any) => item?.status === "likely_misleading"); const isShoppingPage = input.mission?.commerceIntent?.isShoppingPage !== false; return { mode: "mission", headline: isShoppingPage ? `Guardian thinks the best next move for ${input.domain} is to ${recommendation.replaceAll("_", " ")}.` : `Guardian thinks ${input.domain} is not a shopping flow, so it should not run checkout automation here.`, confidence: isShoppingPage ? "high" : "high", inputSummary: `Goal understood: ${input.purchaseGoal}`, reasoning: isShoppingPage ? [ cleanText(input.mission?.trust?.verdict, "Guardian evaluated the trust posture of the current merchant."), signals.length ? `Main manipulation signals: ${signals.slice(0, 3).map((signal: any) => signal.label).join(", ")}.` : "No major manipulation signature was triggered for the current merchant profile.", alternatives.length ? `Guardian found ${alternatives.length} alternative site option${alternatives.length === 1 ? "" : "s"} for comparison.` : "No alternative sites were returned for this mission.", dealItems.length ? misleadingDeals.length ? `Deal-authenticity check flagged ${misleadingDeals.length} likely misleading limited-deal claim${misleadingDeals.length === 1 ? "" : "s"}.` : "Deal-authenticity check did not find high-confidence misleading claim patterns." : "No product-level deal cards were available for authenticity scoring.", ] : [cleanText(input.mission?.commerceIntent?.reason, "No checkout signals were detected on the current page.")], recommendation: cleanText(input.mission?.nextBestAction, "Keep Guardian active through checkout."), trustScore, followUps: isShoppingPage ? [ "Approve account creation only if the site remains the best option after comparison.", "Keep Guardian active until the final payment step to catch late fees.", ] : ["Open a product, cart, booking, or checkout page before rerunning the mission."], }; }