/** * Which Exa `/search` params are unusable per `category`. * * MEASURED against the live API on 2026-08-12, because the published docs are * wrong in both directions and the two categories do NOT share a list: * * | param | category: "people" | category: "company" | * |--------------------|---------------------------|-----------------------| * | includeDomains | 200, then SILENTLY IGNORED| 200, genuinely applied| * | excludeDomains | HTTP 400 | 200, genuinely applied| * | startPublishedDate | HTTP 400 | HTTP 400 | * | endPublishedDate | HTTP 400 | HTTP 400 | * * The docs claim all four 400 for people (wrong: `includeDomains` does not) * and that `excludeDomains` 400s for company (wrong: it works). Verified by * observation, not by reading: `includeDomains: ["github.com"]` on a people * search returned five linkedin.com profiles, while the same filter on a * company search returned zero results, and `excludeDomains: ["stripe.com"]` * removed Stripe from a company search that otherwise ranked it first. * * The silently-ignored case is the dangerous one — a caller gets plausible * results and no signal their filter did nothing — so it is listed here * alongside the hard failures even though it does not error. Domain filters * are deliberately NOT listed for company, because there they work. */ export const EXA_UNSUPPORTED_PARAMS_BY_CATEGORY: Record< string, readonly string[] > = { people: [ "includeDomains", "excludeDomains", "startPublishedDate", "endPublishedDate", ], company: ["startPublishedDate", "endPublishedDate"], }; /** Params unusable for `category`; empty for categories with no restrictions. */ export function unsupportedParamsForCategory( category: string | undefined, ): readonly string[] { if (!category) return []; return EXA_UNSUPPORTED_PARAMS_BY_CATEGORY[category] ?? []; } /** * Delete the params that do not work for this category, returning what was * removed so the caller can report it. Mutates `body`; a no-op for * unrestricted categories. * * Callers deliberately differ in how they use the return value: the agent * workflow step strips silently and reports `droppedParams` (a model cannot * foresee a provider incompatibility, and a hard 400 costs it a whole turn), * while `dench search` refuses outright (a human who typed the flag meant it, * and neither erroring nor silently ignoring it is an acceptable answer). */ export function stripUnsupportedParamsForCategory( body: Record, category: string | undefined, ): string[] { const dropped: string[] = []; for (const key of unsupportedParamsForCategory(category)) { if (body[key] !== undefined) { delete body[key]; dropped.push(key); } } return dropped; }