224 lines · 8.7 KB · TypeScript
RawDownload
import * as Effect from "effect/Effect";import * as Schema from "effect/Schema";import * as SchemaTransformation from "effect/SchemaTransformation";import { TrimmedNonEmptyString } from "./baseSchemas.ts";import { ProviderDriverKind } from "./providerInstance.ts"; export const ProviderOptionDescriptorType = Schema.Literals(["select", "boolean"]);export type ProviderOptionDescriptorType = typeof ProviderOptionDescriptorType.Type; export const ProviderOptionChoice = Schema.Struct({  id: TrimmedNonEmptyString,  label: TrimmedNonEmptyString,  description: Schema.optional(TrimmedNonEmptyString),  isDefault: Schema.optional(Schema.Boolean),});export type ProviderOptionChoice = typeof ProviderOptionChoice.Type; const ProviderOptionDescriptorBase = {  id: TrimmedNonEmptyString,  label: TrimmedNonEmptyString,  description: Schema.optional(TrimmedNonEmptyString),} as const; export const SelectProviderOptionDescriptor = Schema.Struct({  ...ProviderOptionDescriptorBase,  type: Schema.Literal("select"),  options: Schema.Array(ProviderOptionChoice),  currentValue: Schema.optional(TrimmedNonEmptyString),  promptInjectedValues: Schema.optional(Schema.Array(TrimmedNonEmptyString)),});export type SelectProviderOptionDescriptor = typeof SelectProviderOptionDescriptor.Type; export const BooleanProviderOptionDescriptor = Schema.Struct({  ...ProviderOptionDescriptorBase,  type: Schema.Literal("boolean"),  currentValue: Schema.optional(Schema.Boolean),});export type BooleanProviderOptionDescriptor = typeof BooleanProviderOptionDescriptor.Type; export const ProviderOptionDescriptor = Schema.Union([  SelectProviderOptionDescriptor,  BooleanProviderOptionDescriptor,]);export type ProviderOptionDescriptor = typeof ProviderOptionDescriptor.Type; export const ProviderOptionSelectionValue = Schema.Union([TrimmedNonEmptyString, Schema.Boolean]);export type ProviderOptionSelectionValue = typeof ProviderOptionSelectionValue.Type; export const ProviderOptionSelection = Schema.Struct({  id: TrimmedNonEmptyString,  value: ProviderOptionSelectionValue,});export type ProviderOptionSelection = typeof ProviderOptionSelection.Type; /** * Legacy on-disk shape for provider option selections, kept readable by the * decoder so we can tolerate stored data written before the v3 array shape. * * Persisted historically as `{ effort: "max", fastMode: true, ... }` inside * `modelSelection.options`. Migration 026 rewrites stored rows to the * canonical array shape, but we still see the legacy form in: *   - `settings.json` files from older client builds, *   - SQLite databases that have not yet run migration 026, *   - any future regression that re-introduces the legacy shape. */const LegacyProviderOptionSelectionsObject = Schema.Record(Schema.String, Schema.Unknown); const ProviderOptionSelectionsFromLegacyObject = LegacyProviderOptionSelectionsObject.pipe(  Schema.decodeTo(    Schema.Array(ProviderOptionSelection),    SchemaTransformation.transformOrFail({      decode: (record) => Effect.succeed(coerceLegacyOptionsObjectToArray(record)),      encode: (selections) => Effect.succeed(canonicalSelectionsToLegacyObject(selections)),    }),  ),); /** * Schema for the `options` field of every `ModelSelection` variant. * * Accepts both: *   - the canonical array shape `Array<{ id, value }>` (preferred), and *   - the legacy object shape `Record<string, string | boolean | …>` from *     pre-migration data. * * Always normalizes to the canonical array on decode and re-encodes as the * canonical array, so any legacy storage gets cleaned up the next time the * containing record is written back. */export const ProviderOptionSelections = Schema.Union([  Schema.Array(ProviderOptionSelection),  ProviderOptionSelectionsFromLegacyObject,]);export type ProviderOptionSelections = typeof ProviderOptionSelections.Type; function coerceLegacyOptionsObjectToArray(  record: Record<string, unknown>,): ReadonlyArray<ProviderOptionSelection> {  const entries: Array<ProviderOptionSelection> = [];  for (const [rawKey, rawValue] of Object.entries(record)) {    const id = typeof rawKey === "string" ? rawKey.trim() : "";    if (id.length === 0) continue;    if (typeof rawValue === "string") {      const trimmed = rawValue.trim();      if (trimmed.length > 0) entries.push({ id, value: trimmed });    } else if (typeof rawValue === "boolean") {      entries.push({ id, value: rawValue });    }    // Drop anything else (numbers, null, nested objects/arrays) to match the    // permissive normalization performed by migration 026.  }  return entries;} function canonicalSelectionsToLegacyObject(  selections: ReadonlyArray<ProviderOptionSelection>,): Record<string, string | boolean> {  const out: Record<string, string | boolean> = {};  for (const { id, value } of selections) {    out[id] = value;  }  return out;} export const ModelCapabilities = Schema.Struct({  optionDescriptors: Schema.optional(Schema.Array(ProviderOptionDescriptor)),});export type ModelCapabilities = typeof ModelCapabilities.Type; /** * A user-authored custom model. `name` and `capabilities` are optional so a * bare slug keeps its driver-default presentation; when `capabilities` is * set, its descriptors replace the driver default in the model picker. */export const CustomModelEntry = Schema.Struct({  slug: TrimmedNonEmptyString,  name: Schema.optional(TrimmedNonEmptyString),  capabilities: Schema.optional(ModelCapabilities),});export type CustomModelEntry = typeof CustomModelEntry.Type; /** On-disk custom model setting: the legacy bare slug, or a full entry. */export const CustomModelSetting = Schema.Union([Schema.String, CustomModelEntry]);export type CustomModelSetting = typeof CustomModelSetting.Type; const CODEX_DRIVER_KIND = ProviderDriverKind.make("codex");const CLAUDE_DRIVER_KIND = ProviderDriverKind.make("claudeAgent");const CURSOR_DRIVER_KIND = ProviderDriverKind.make("cursor");const GROK_DRIVER_KIND = ProviderDriverKind.make("grok");const OPENCODE_DRIVER_KIND = ProviderDriverKind.make("opencode"); export const DEFAULT_MODEL = "gpt-5.6-sol"; /** * Codex default-model preference, most preferred first. The provider snapshot * marks the first of these present in the live `model/list` response as * default; when none are available, Codex's own `isDefault` flag wins. */export const PREFERRED_DEFAULT_CODEX_MODELS: ReadonlyArray<string> = [  "gpt-5.6-sol",  "gpt-5.6-terra",];export const DEFAULT_TEXT_GENERATION_MODEL = "gpt-5.6-luna";/** Keep the official Antigravity session's current model. Never send this ID to ACP. */export const ANTIGRAVITY_DEFAULT_MODEL = "antigravity-default";export const DEFAULT_TEXT_GENERATION_REASONING_EFFORT = "low"; export const DEFAULT_MODEL_BY_PROVIDER: Partial<Record<ProviderDriverKind, string>> = {  [CODEX_DRIVER_KIND]: DEFAULT_MODEL,  [CLAUDE_DRIVER_KIND]: "claude-sonnet-5",  [CURSOR_DRIVER_KIND]: "auto",  // Product slug, not an ACP model id. The Grok adapter treats it as "the session's current model".  [GROK_DRIVER_KIND]: "grok-build",  [OPENCODE_DRIVER_KIND]: "openai/gpt-5",  [ProviderDriverKind.make("antigravity")]: ANTIGRAVITY_DEFAULT_MODEL,}; /** Per-provider text generation model defaults. */export const DEFAULT_TEXT_GENERATION_MODEL_BY_PROVIDER: Partial<  Record<ProviderDriverKind, string>> = {  [CODEX_DRIVER_KIND]: DEFAULT_TEXT_GENERATION_MODEL,  [ProviderDriverKind.make("antigravity")]: ANTIGRAVITY_DEFAULT_MODEL,  [CLAUDE_DRIVER_KIND]: "claude-haiku-4-5",  [CURSOR_DRIVER_KIND]: "composer-2",  [OPENCODE_DRIVER_KIND]: "openai/gpt-5",}; export const MODEL_SLUG_ALIASES_BY_PROVIDER: Partial<  Record<ProviderDriverKind, Record<string, string>>> = {  [CODEX_DRIVER_KIND]: {    "gpt-5-codex": "gpt-5.4",    "5.4": "gpt-5.4",    "5.3": "gpt-5.3-codex",    "gpt-5.3": "gpt-5.3-codex",    "5.3-spark": "gpt-5.3-codex-spark",    "gpt-5.3-spark": "gpt-5.3-codex-spark",  },  [CLAUDE_DRIVER_KIND]: {},  [CURSOR_DRIVER_KIND]: {    composer: "composer-2",    "composer-1.5": "composer-1.5",    "composer-1": "composer-1.5",    "opus-4.6-thinking": "claude-opus-4-6",    "opus-4.6": "claude-opus-4-6",    "sonnet-4.6-thinking": "claude-sonnet-4-6",    "sonnet-4.6": "claude-sonnet-4-6",    "opus-4.5-thinking": "claude-opus-4-5",    "opus-4.5": "claude-opus-4-5",  },  [OPENCODE_DRIVER_KIND]: {},}; // ── Provider display names ──────────────────────────────────────────── export const PROVIDER_DISPLAY_NAMES: Partial<Record<ProviderDriverKind, string>> = {  [ProviderDriverKind.make("antigravity")]: "Antigravity",  [CODEX_DRIVER_KIND]: "Codex",  [CLAUDE_DRIVER_KIND]: "Claude",  [CURSOR_DRIVER_KIND]: "Cursor",  [GROK_DRIVER_KIND]: "Grok",  [OPENCODE_DRIVER_KIND]: "OpenCode",};