/** * Competitor discovery: the ONE judge-model call M4 makes (M4). * * Billed to the cost guard's setup budget (hard rule #5). It never throws - a * cap hit or a judge error degrades to an empty competitor list with a reason, * so discovery always returns a usable profile. */ import { CostGuard } from '../costs.js'; import type { BusinessType, JudgeClient } from '../types.js'; export interface CompetitorInput { brand: string; /** * Other names the brand goes by (the profile's extracted `aliases`). Named in * the prompt AND used to filter the answer: a judge asked to exclude only * `doremusic` still returns "Do Re Müzik Market", which is the brand itself. */ aliases?: string[]; category?: string; offerings?: string[]; /** * The brand's primary market, from the profile's extracted `locale` (e.g. * `tr`, `en-US`). Without it the judge defaults to US/global brands, which * for a local retailer yields rivals that never appear in that market's * answers - a share-of-voice table of all zeroes. */ locale?: string; } export interface CompetitorDeps { judge: JudgeClient; guard: CostGuard; /** Projected setup cost for authorization; defaults to a per-model estimate. */ projectedCostUsd?: number; } export interface CompetitorResult { competitors: string[]; /** How the judge classified the business (M5a); absent when unknown. */ businessType?: BusinessType; /** Set when the call was skipped or failed (cap hit or judge error). */ skipped?: string; } /** * Drop names that are the brand under another spelling. * * Live 2026-07-20, every judge returned the brand as its own rival ("Do Re * Müzik Market" for doremusic), which put the brand in its own share-of-voice * table. Matching is the shared Unicode-aware one (so case, accents and * boundaries behave like scoring's detector), applied BOTH ways - the returned * name may wrap a brand term ("Do Re Müzik Market") or be wrapped by one ("Do * Re" for "Do Re Müzik") - plus a squeezed comparison that catches respacing * ("Do Re Music" -> "doremusic"). Whole terms only: "Ace Rental" survives a * brand called "Ace Hardware". A brand whose alias is one generic word will * over-match, which is the same ambiguity M7 flags rather than a new risk. */ export declare function dropSelfReferences(names: string[], terms: string[]): string[]; /** * Parse a competitor list from model output. Handles a JSON array or a * newline/comma list with bullets or numbering (lesson #4: parse real shapes). * * `selfTerms` (the brand and its aliases) are removed BEFORE the cap is * applied, so a judge that opens with three spellings of the brand still yields * a full list of rivals rather than spending slots on itself. */ export declare function parseCompetitors(text: string, selfTerms?: string[]): string[]; export declare function parseDiscovery(text: string, selfTerms?: string[]): { businessType?: BusinessType; competitors: string[]; }; /** Discover competitor brand names via one guarded judge call. */ export declare function discoverCompetitors(input: CompetitorInput, deps: CompetitorDeps): Promise;