export type AiProtocol = 'openai-chat' | 'openai-responses' | 'anthropic' export type AiAuthStyle = 'bearer' | 'x-api-key' | 'both' | 'none' export type AiBillingMode = 'payg' | 'plan' | 'local' export type AiModelCapability = 'chat' | 'reasoning' | 'plan' export interface AiModelConfig { /** Stable UI identity. Multiple routes may point at the same upstream model id. */ id: string /** Exact model id sent to the provider. */ model: string label: string capability: AiModelCapability billing: AiBillingMode /** Optional per-model route overrides for subscription/plan endpoints. */ protocol?: AiProtocol baseUrl?: string } export interface AiProviderConfig { id: string name: string presetId?: string protocol: AiProtocol authStyle: AiAuthStyle baseUrl: string allowInsecureHttp: boolean models: AiModelConfig[] defaultModelId: string enabled: boolean /** Runtime-only flag. The API key itself never leaves the Rust process after saving. */ hasApiKey?: boolean } export type AiAssistantAction = 'explain' | 'command' | 'diagnose' | 'execute' export interface AiTerminalContext { selection: string viewport: string } export interface AiCompleteRequest { provider: AiProviderConfig model: AiModelConfig action: AiAssistantAction prompt: string context: string } export interface AiCompleteResponse { success: boolean text?: string model?: string redactedCount?: number error?: string } export interface AiModelListResponse { success: boolean models?: string[] error?: string } /** Connection test credentials are transient and are never persisted by the test command. */ export interface AiProviderTestRequest { provider: AiProviderConfig model: AiModelConfig apiKey?: string } export interface AiProviderTestResponse { success: boolean status?: number model?: string latencyMs?: number answer?: string error?: string } export interface AiCatalogDocument { source: string data: unknown } export interface AiCatalogFetchResponse { success: boolean documents?: AiCatalogDocument[] errors?: string[] error?: string }