/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ /** * Types for the LLM chat integration. */ import type { RepairScope, ScriptDiagnostic } from './script-diagnostics.js'; export type MessageRole = 'user' | 'assistant' | 'system'; export interface ChatMessage { id: string; role: MessageRole; content: string; /** Timestamp in ms */ createdAt: number; /** Parsed code blocks from assistant messages */ codeBlocks?: CodeBlock[]; /** Execution results for code blocks in this message */ execResults?: Map; /** Attached files (user messages only) */ attachments?: FileAttachment[]; } export interface CodeBlock { /** Index within the message */ index: number; /** Language hint from the code fence (e.g. 'js', 'typescript') */ language: string; /** The code content */ code: string; } export interface CodeExecResult { status: 'running' | 'success' | 'error'; logs?: Array<{ level: string; args: unknown[] }>; value?: unknown; error?: string; durationMs?: number; } export interface ScriptEditorSelection { from: number; to: number; } export interface ScriptEditorTextChange { from: number; to: number; insert: string; } interface ScriptEditBase { opId: string; baseRevision: number; groupId?: string; scope?: RepairScope; atomic?: boolean; targetRootCause?: string; } export interface ScriptEditInsertOp extends ScriptEditBase { type: 'insert'; at: number; text: string; } export interface ScriptEditReplaceRangeOp extends ScriptEditBase { type: 'replaceRange'; from: number; to: number; text: string; expectedText?: string; } export interface ScriptEditReplaceSelectionOp extends ScriptEditBase { type: 'replaceSelection'; text: string; } export interface ScriptEditAppendOp extends ScriptEditBase { type: 'append'; text: string; } export interface ScriptEditReplaceAllOp extends ScriptEditBase { type: 'replaceAll'; text: string; } export type ScriptEditOperation = | ScriptEditInsertOp | ScriptEditReplaceRangeOp | ScriptEditReplaceSelectionOp | ScriptEditAppendOp | ScriptEditReplaceAllOp; export type ChatRepairReason = 'runtime' | 'preflight' | 'patch-conflict' | 'patch-apply'; export interface ChatRepairRequest { error: string; reason: ChatRepairReason; diagnostics?: ScriptDiagnostic[]; staleCodeBlock?: string; includeSelection?: boolean; requestedRepairScope?: RepairScope; rootCauseKey?: string; } export interface FileAttachment { id: string; name: string; type: string; size: number; /** Parsed CSV rows (if CSV file) */ csvData?: Record[]; /** Column names (if CSV file) */ csvColumns?: string[]; /** Raw text content */ textContent?: string; /** Base64-encoded image data (for image attachments) */ imageBase64?: string; /** Whether this is an image attachment */ isImage?: boolean; /** Base64-encoded PDF data (for PDF attachments — Anthropic native document blocks) */ pdfBase64?: string; /** Whether this is a PDF attachment */ isPdf?: boolean; /** Whether this is a binary spreadsheet (xlsx/xls/ods) that we can't parse here yet. */ isSpreadsheetBinary?: boolean; } export type ModelTier = 'free' | 'byok'; /** * Where requests for this model are routed. * - 'proxy': through the server-side proxy (free models) * - 'anthropic': direct browser-to-Anthropic API (user's own key) * - 'openai': direct browser-to-OpenAI API (user's own key) */ export type ModelSource = 'proxy' | 'anthropic' | 'openai'; /** Relative cost indicator for paid models */ export type ModelCost = '$' | '$$' | '$$$'; export interface LLMModel { id: string; name: string; provider: string; tier: ModelTier; /** Where requests are routed — proxy (free) or direct to provider (BYOK) */ source: ModelSource; contextWindow: number; /** Whether this model accepts image inputs in chat content */ supportsImages: boolean; /** Whether this model should receive uploaded file context */ supportsFileAttachments: boolean; /** Notes shown in model selector */ notes?: string; /** Relative cost indicator (BYOK models only) */ cost?: ModelCost; /** OpenAI API variant: 'chat' (default) or 'responses' (Codex-style models) */ openaiApi?: 'chat' | 'responses'; /** * Whether the model accepts the classic sampling parameters (`temperature`, * `top_p`, `top_k`). Default: true. Set to `false` for models that reject * them (Anthropic Claude Opus 4.7 and later — see * https://platform.claude.com/docs/en/about-claude/models/whats-new-claude-4-7). * When `false`, the stream client omits these params from the request body. */ acceptsSamplingParams?: boolean; } export type ChatStatus = 'idle' | 'sending' | 'streaming' | 'error';