/** * `squad models` — model catalog diagnostics (issue #1080 / #1183). * * `squad models refresh` reconciles Squad's committed SEED catalog against the * live, CLI-reachable model list. Sourcing is AUTH-FREE-FIRST HYBRID: * * 1. CANONICAL (optional auth): `GET https://api.githubcopilot.com/models` * with `Copilot-Integration-Id: copilot-cli`, using `gh auth token` * directly as a Bearer token (verified auth spike B0 — no * copilot_internal token exchange needed). Provides ids + * `model_picker_category`. * 2. GRACEFUL FALLBACK (no auth): the public github/docs YAML * `models-and-pricing.yml` — provides category + pricing + release_status. * 3. The committed {@link MODEL_CATALOG} is a SEED, never the sole truth. * * Discovered internal ids are written ONLY to the gitignored local cache * (`.squad/.cache/models.json`) — never persisted to committed/deployed files. * * The token value is NEVER printed or logged. */ import { type ModelInfo, type GitHubModelCategory } from '@bradygaster/squad-sdk/config'; /** A model discovered from a live source (API or docs YAML). */ export interface DiscoveredModel { id: string; githubCategory?: GitHubModelCategory; releaseStatus?: string; /** Best-effort pricing from the docs YAML (per 1M tokens); never hardcoded. */ pricing?: { input?: string; output?: string; }; } /** Which source ultimately produced the discovered set. */ export type RefreshSource = 'api' | 'docs-fallback' | 'seed-only'; export interface RefreshResult { source: RefreshSource; models: DiscoveredModel[]; /** discovered ids absent from the seed */ added: string[]; /** seed ids absent from the discovered set (candidates to prune) */ removed: string[]; /** discovered models that have no pricing from the docs YAML — visible signal for new/unpriced models */ unpricedIds: string[]; } /** Injectable side effects (network + auth), so both paths are unit-testable. */ export interface RefreshDeps { /** Returns a Copilot token, or null when `gh` is unavailable/unauthenticated. */ getToken: () => Promise; /** Fetches the Copilot models API. MUST reject on 401/400/network. */ fetchApiModels: (token: string) => Promise; /** Fetches the raw docs YAML text. */ fetchDocsYaml: () => Promise; /** The committed seed catalog. */ seed: ModelInfo[]; } /** * Parse the Copilot models API response into CLI-reachable, picker-enabled * models. Shape: `{ data: [{ id, model_picker_category, model_picker_enabled }] }`. */ export declare function parseApiModels(json: unknown): DiscoveredModel[]; /** * Derive the candidate catalog id from a docs YAML display name deterministically. * Steps: * 1. Strip markdown footnote markers `[^...]` — these are promo markers on the SAME model * 2. Strip literal `(` and `)` chars but KEEP the words inside — parenthetical qualifiers * denote a DIFFERENT SKU (e.g. fast-mode, preview), so they must NOT collapse to the * base model id. The extra words produce a longer, non-catalog-matching id instead. * 3. Lowercase and trim * 4. Collapse any run of whitespace to a single hyphen * * Examples: * "GPT-5.6 Luna" → "gpt-5.6-luna" (matches catalog) * "Claude Sonnet 5[^sonnet-5-promo]" → "claude-sonnet-5" (footnote stripped, matches) * "Claude Opus 4.8" → "claude-opus-4.8" (matches catalog → $5/$25) * "Claude Opus 4.8 (fast mode) (prev)" → "claude-opus-4.8-fast-mode-prev" (ignored — not in catalog) * "Gemini 3.1 Pro" → "gemini-3.1-pro" (matches catalog) */ export declare function normalizeDisplayName(name: string): string; /** * Escape hatch for docs display names whose normalization cannot derive the * correct catalog id. Keys are the NORMALIZED form (output of normalizeDisplayName). * Add an entry only when normalization produces the wrong id for a specific name; * the canonical approach is zero overrides — algorithmic normalization handles all * current model names without any manual mappings. */ export declare const DOCS_NAME_OVERRIDES: Record; /** * Minimal, tolerant parser for the docs `models-and-pricing.yml` flat list of * `- model:` blocks. Avoids adding a YAML dependency; unmatched names are kept * by their normalized id (filtering against the live catalog happens in the caller * via enrichWithPricing or the docs-fallback seed filter). */ export declare function parseDocsYaml(text: string): DiscoveredModel[]; /** * Enrich API-discovered models with pricing (and releaseStatus) from the docs * YAML, joined by model id. The API stays authoritative for id/category/ * reachability; docs only SUPPLIES `pricing`/`releaseStatus` for ids the API * already returned. No ids are added, and unmatched API models are returned * unchanged. Pure and side-effect-free. */ export declare function enrichWithPricing(apiModels: DiscoveredModel[], docsModels: DiscoveredModel[]): DiscoveredModel[]; /** * Core refresh logic. Tries the canonical API first; on ANY failure (no gh, * 401/400, network) falls back to the public docs YAML; if that also yields * nothing usable, reports seed-only. NEVER hard-fails on the API arm. */ export declare function refreshModelCatalog(deps: RefreshDeps): Promise; export declare function runModels(cwd: string, subArgs: string[]): Promise; //# sourceMappingURL=models.d.ts.map