Spaces:
Sleeping
Sleeping
File size: 8,437 Bytes
5743bc2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | type ScanReport = Record<string, any>;
type FeeEstimate = Record<string, any>;
type MissionPayload = Record<string, any>;
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."],
};
}
|