/** Provider-neutral types used by the chat engine, router and UI. */ export type Provider = "openai" | "anthropic" | "local"; export type Capability = "text" | "vision" | "audioInput" | "audioOutput" | "imageGeneration" | "reasoning" | "tools" | "structuredOutput" | "streaming" | "longContext"; export type CapValue = boolean | "unknown"; export interface AIModel { id: string; provider: Provider; displayName: string; kind: string; api?: "responses" | "chat" | "messages" | null; status: "verified" | "unavailable" | "unsupported" | "untested"; available: boolean; tested: boolean; unavailableReason?: string | null; capabilities: Partial>; contextWindow?: number | null; maxOutputTokens?: number | null; pricing?: { input?: number; cachedInput?: number; output?: number; notes?: string; [k: string]: unknown } | null; benchmark?: { tests: number; passed: number; qualityScore: number | null; p50LatencyMs: number | null; p50TtftMs: number | null; totalCostUsd: number; avgOutputTokens: number | null; perTest: Record; errors: string[]; } | null; hidden: boolean; favorite: boolean; lastVerifiedAt?: string | null; } // ---------------------------------------------------------------- message parts (stored in messages.parts) export type Part = | { type: "text"; text: string } | { type: "reasoning"; text: string; summarized?: boolean } | { type: "image"; attachmentId: string; mime: string; name?: string; url?: string } | { type: "file"; attachmentId: string; mime: string; name: string; size?: number; pages?: number } | { type: "tool_call"; id: string; name: string; input: unknown; status: "pending_approval" | "running" | "ok" | "error" | "denied"; output?: unknown; durationMs?: number; error?: string } | { type: "artifact"; artifactId: string; title: string; artifactType: string; language?: string } | { type: "citation"; url: string; title?: string; snippet?: string } | { type: "generated_image"; attachmentId: string; prompt: string; model: string }; export type Role = "user" | "assistant" | "system" | "tool"; export interface UnifiedMessage { role: Role; parts: Part[]; id?: string } export interface ToolDef { name: string; description: string; inputSchema: Record; // JSON schema object } export interface ChatSettings { effort?: "low" | "medium" | "high" | "xhigh" | "max"; maxOutputTokens?: number; temperature?: number; // only honoured where the provider still accepts it reasoningSummary?: boolean; toolsEnabled?: string[]; } export interface ChatRequest { model: AIModel; system?: string; messages: UnifiedMessage[]; tools?: ToolDef[]; settings?: ChatSettings; signal?: AbortSignal; /** resolver for attachments referenced in parts → {bytes, mime} */ loadAttachment: (id: string) => Promise<{ data: Buffer; mime: string; name: string; text?: string | null } | null>; } export interface Usage { input: number; output: number; cached?: number; reasoning?: number } export type StreamEvent = | { type: "text"; delta: string } | { type: "reasoning"; delta: string } | { type: "tool_call"; id: string; name: string; input: unknown } | { type: "usage"; usage: Usage } | { type: "stop"; reason: string; details?: unknown } | { type: "error"; message: string; status?: number; retryable?: boolean } | { type: "meta"; data: Record }; export interface ProviderAdapter { provider: Provider; stream(req: ChatRequest): AsyncGenerator; }