1403 lines · 60.6 KB · TypeScript
RawDownload
import { SshDeviceHostConfigs } from "./device.ts";import * as Effect from "effect/Effect";import * as Duration from "effect/Duration";import * as Schema from "effect/Schema";import * as SchemaTransformation from "effect/SchemaTransformation";import {  ForwardCompatibleNullable,  ProjectId,  TrimmedNonEmptyString,  TrimmedString,} from "./baseSchemas.ts";import { UsageLimitSourceId } from "./usageLimitSourceId.ts";import { EnvironmentMachineKind, ThreadEnvMode } from "./environment.ts";import { KeybindingShortcut } from "./keybindings.ts";import {  CustomModelSetting,  DEFAULT_TEXT_GENERATION_MODEL,  DEFAULT_TEXT_GENERATION_REASONING_EFFORT,  ProviderOptionSelections,} from "./model.ts";import { ModelSelection, ProjectScript } from "./orchestration.ts";import { BrowserProfile, BrowserProfileId, DEFAULT_BROWSER_PROFILE_ID } from "./browserProfile.ts";import {  DEFAULT_PREVIEW_APPEARANCE,  DEFAULT_PREVIEW_ZOOM_FACTOR,  FILL_PREVIEW_VIEWPORT,  PreviewAppearancePreference,  PreviewViewportSetting,  PreviewZoomFactor,} from "./preview.ts";import {  ProviderInstanceConfig,  ProviderInstanceId,  type ProviderDriverKind,} from "./providerInstance.ts";import { PullRequestMergeMethod } from "./pullRequest.ts"; // ── Client Settings (local-only) ─────────────────────────────── export const TimestampFormat = Schema.Literals(["locale", "12-hour", "24-hour"]);export type TimestampFormat = typeof TimestampFormat.Type;const DEFAULT_TIMESTAMP_FORMAT: TimestampFormat = "locale"; export const DiffLayout = Schema.Literals(["stacked", "split"]);export type DiffLayout = typeof DiffLayout.Type;const DEFAULT_DIFF_LAYOUT: DiffLayout = "stacked"; export const SidebarProjectSortOrder = Schema.Literals(["updated_at", "created_at", "manual"]);export type SidebarProjectSortOrder = typeof SidebarProjectSortOrder.Type;export const DEFAULT_SIDEBAR_PROJECT_SORT_ORDER: SidebarProjectSortOrder = "updated_at"; export const SidebarThreadSortOrder = Schema.Literals(["updated_at", "created_at"]);export type SidebarThreadSortOrder = typeof SidebarThreadSortOrder.Type;export const DEFAULT_SIDEBAR_THREAD_SORT_ORDER: SidebarThreadSortOrder = "updated_at"; export const SidebarProjectGroupingMode = Schema.Literals([  "repository",  "repository_path",  "separate",]);export type SidebarProjectGroupingMode = typeof SidebarProjectGroupingMode.Type;const DEFAULT_SIDEBAR_PROJECT_GROUPING_MODE: SidebarProjectGroupingMode = "repository";export const MIN_SIDEBAR_THREAD_PREVIEW_COUNT = 1;export const MAX_SIDEBAR_THREAD_PREVIEW_COUNT = 15;export const SidebarThreadPreviewCount = Schema.Int.check(  Schema.isBetween({    minimum: MIN_SIDEBAR_THREAD_PREVIEW_COUNT,    maximum: MAX_SIDEBAR_THREAD_PREVIEW_COUNT,  }),);export type SidebarThreadPreviewCount = typeof SidebarThreadPreviewCount.Type;const DEFAULT_SIDEBAR_THREAD_PREVIEW_COUNT: SidebarThreadPreviewCount = 6;export const MIN_SIDEBAR_AUTO_SETTLE_AFTER_DAYS = 1;export const MAX_SIDEBAR_AUTO_SETTLE_AFTER_DAYS = 90;export const SidebarAutoSettleAfterDays = Schema.Number.check(  Schema.isBetween({    minimum: MIN_SIDEBAR_AUTO_SETTLE_AFTER_DAYS,    maximum: MAX_SIDEBAR_AUTO_SETTLE_AFTER_DAYS,  }),);export type SidebarAutoSettleAfterDays = typeof SidebarAutoSettleAfterDays.Type;const DEFAULT_SIDEBAR_AUTO_SETTLE_AFTER_DAYS: SidebarAutoSettleAfterDays = 3;export const MIN_GLASS_OPACITY = 40;export const MAX_GLASS_OPACITY = 100;export const GlassOpacity = Schema.Int.check(  Schema.isBetween({    minimum: MIN_GLASS_OPACITY,    maximum: MAX_GLASS_OPACITY,  }),);export type GlassOpacity = typeof GlassOpacity.Type;const DEFAULT_GLASS_OPACITY: GlassOpacity = 80; export const MIN_APPEARANCE_CONTRAST = 50;export const MAX_APPEARANCE_CONTRAST = 200;export const AppearanceContrast = Schema.Int.check(  Schema.isBetween({ minimum: MIN_APPEARANCE_CONTRAST, maximum: MAX_APPEARANCE_CONTRAST }),);export type AppearanceContrast = typeof AppearanceContrast.Type;const DEFAULT_APPEARANCE_CONTRAST: AppearanceContrast = 100;export const MIN_PANEL_ANIMATION_DURATION_MS = 0;export const MAX_PANEL_ANIMATION_DURATION_MS = 400;export const PanelAnimationDurationMs = Schema.Int.check(  Schema.isBetween({    minimum: MIN_PANEL_ANIMATION_DURATION_MS,    maximum: MAX_PANEL_ANIMATION_DURATION_MS,  }),);export type PanelAnimationDurationMs = typeof PanelAnimationDurationMs.Type;const DEFAULT_PANEL_ANIMATION_DURATION_MS: PanelAnimationDurationMs = 0;/** * Font size preferences, in CSS pixels. The ranges are deliberately narrow: * the interface size scales every rem-based dimension in the app, so the * bounds keep layouts intact rather than offering unusable extremes. */export const MIN_INTERFACE_FONT_SIZE = 12;export const MAX_INTERFACE_FONT_SIZE = 20;export const InterfaceFontSize = Schema.Int.check(  Schema.isBetween({ minimum: MIN_INTERFACE_FONT_SIZE, maximum: MAX_INTERFACE_FONT_SIZE }),);export type InterfaceFontSize = typeof InterfaceFontSize.Type;export const DEFAULT_INTERFACE_FONT_SIZE: InterfaceFontSize = 16; export const MIN_PROMPT_FONT_SIZE = 12;export const MAX_PROMPT_FONT_SIZE = 20;export const PromptFontSize = Schema.Int.check(  Schema.isBetween({ minimum: MIN_PROMPT_FONT_SIZE, maximum: MAX_PROMPT_FONT_SIZE }),);export type PromptFontSize = typeof PromptFontSize.Type;export const DEFAULT_PROMPT_FONT_SIZE: PromptFontSize = 14; export const MIN_CODE_FONT_SIZE = 10;export const MAX_CODE_FONT_SIZE = 18;export const CodeFontSize = Schema.Int.check(  Schema.isBetween({ minimum: MIN_CODE_FONT_SIZE, maximum: MAX_CODE_FONT_SIZE }),);export type CodeFontSize = typeof CodeFontSize.Type;export const DEFAULT_CODE_FONT_SIZE: CodeFontSize = 13; export const MIN_TERMINAL_FONT_SIZE = 8;export const MAX_TERMINAL_FONT_SIZE = 20;export const TerminalFontSize = Schema.Int.check(  Schema.isBetween({ minimum: MIN_TERMINAL_FONT_SIZE, maximum: MAX_TERMINAL_FONT_SIZE }),);export type TerminalFontSize = typeof TerminalFontSize.Type;const DEFAULT_TERMINAL_FONT_SIZE: TerminalFontSize = 12; export const EnvironmentIdentificationMode = Schema.Literals(["artwork", "pill", "none"]);export type EnvironmentIdentificationMode = typeof EnvironmentIdentificationMode.Type;export const DEFAULT_ENVIRONMENT_IDENTIFICATION_MODE: EnvironmentIdentificationMode = "artwork"; export const SnapShotKeyChord = KeybindingShortcut.check(  Schema.makeFilter(    (shortcut) =>      shortcut.metaKey ||      shortcut.ctrlKey ||      shortcut.shiftKey ||      shortcut.altKey ||      shortcut.modKey ||      "Snapshot shortcut requires a modifier.",  ),);export type SnapShotKeyChord = typeof SnapShotKeyChord.Type;export const SNAP_SHOT_MODIFIERS = ["shift", "meta", "control", "alt"] as const;export const SnapShotModifier = Schema.Literals(SNAP_SHOT_MODIFIERS);export type SnapShotModifier = typeof SnapShotModifier.Type;export const SnapShotShortcut = Schema.Union([  Schema.Struct({ kind: Schema.Literal("both-shift-keys") }),  Schema.Struct({ kind: Schema.Literal("modifier-pair"), modifier: SnapShotModifier }),  SnapShotKeyChord,]);export type SnapShotShortcut = typeof SnapShotShortcut.Type;export const SnapShotSound = Schema.Literals(["soft-pop", "camera-shutter"]);export type SnapShotSound = typeof SnapShotSound.Type;const DEFAULT_SNAP_SHOT_SOUND: SnapShotSound = "soft-pop"; export type SnapShotModifierPairShortcut = Extract<SnapShotShortcut, { readonly kind: string }>; export function isModifierPairShortcut(  shortcut: SnapShotShortcut,): shortcut is SnapShotModifierPairShortcut {  return "kind" in shortcut;} export function snapShotShortcutModifierPair(  shortcut: SnapShotModifierPairShortcut,): SnapShotModifier {  return shortcut.kind === "both-shift-keys" ? "shift" : shortcut.modifier;} const APPLE_MODIFIER_LABELS: Record<SnapShotModifier, string> = {  shift: "Shift",  meta: "Command",  control: "Control",  alt: "Option",};const OTHER_MODIFIER_LABELS: Record<SnapShotModifier, string> = {  shift: "Shift",  meta: "Super",  control: "Ctrl",  alt: "Alt",}; export function snapShotModifierPairLabel(modifier: SnapShotModifier, apple: boolean): string {  const label = (apple ? APPLE_MODIFIER_LABELS : OTHER_MODIFIER_LABELS)[modifier];  return `${label} + ${label}`;}const DEFAULT_SNAP_SHOT_SHORTCUT: SnapShotShortcut = {  kind: "both-shift-keys",}; export const QuitConfirmationMode = Schema.Literals(["direct", "hold", "double-click"]);export type QuitConfirmationMode = typeof QuitConfirmationMode.Type;const DEFAULT_QUIT_CONFIRMATION_MODE: QuitConfirmationMode = "hold"; const LegacyConfirmQuit = Schema.Boolean.pipe(  Schema.decodeTo(    QuitConfirmationMode,    SchemaTransformation.transform({      decode: (confirmQuit): QuitConfirmationMode => (confirmQuit ? "hold" : "direct"),      encode: (mode) => mode === "hold",    }),  ),); const QuitConfirmationModeSetting = Schema.Union([QuitConfirmationMode, LegacyConfirmQuit]); /** * A user-chosen font family (a single name or a comma-separated list). Empty * means "use the app default"; clients compose their own fallback stacks. */export const FontFamilyPreference = Schema.String.check(Schema.isMaxLength(200));export type FontFamilyPreference = typeof FontFamilyPreference.Type; /** * The environment's theme, set with `t3 theme set <id>`. Each client applies * it once per value — live when connected, on its next connect otherwise — so * setting it switches every client, while a theme a user picks in Settings * afterwards sticks until the next set. Empty means "no environment theme", * which is also how it is cleared. */export const DefaultThemePreference = Schema.String.check(Schema.isMaxLength(64));// Deliberately absent from ServerSettingsPatch: `t3 theme set` checks that an// id is syntactically valid and actually resolvable, and a generic RPC patch// would let a client write a theme no client can resolve, bypassing both.export type DefaultThemePreference = typeof DefaultThemePreference.Type; /** * Defaults for the in-app preview browser, applied whenever a tab is opened * without an explicit viewport/zoom/appearance — by the user opening a browser * tab, or by an agent calling `preview_open` with no size. Recording quality is * client-local for the same reason: the Chromium guest being captured belongs * to the desktop app. */export const DEFAULT_BROWSER_VIEWPORT: PreviewViewportSetting = FILL_PREVIEW_VIEWPORT;export const DEFAULT_BROWSER_AUTO_SHOW_FLOATING_PREVIEW = true;export const BROWSER_RECORDING_FRAME_RATES = [30, 60] as const;export const BrowserRecordingFrameRate = Schema.Literals(BROWSER_RECORDING_FRAME_RATES);export type BrowserRecordingFrameRate = typeof BrowserRecordingFrameRate.Type;export const DEFAULT_BROWSER_RECORDING_FRAME_RATE: BrowserRecordingFrameRate = 30;/** * Where a clicked link goes: the OS default browser, or a tab in the in-app * browser beside the thread. "system" is the default because that is what * every link did before the setting existed. */export const BrowserLinkTarget = Schema.Literals(["system", "app"]);export type BrowserLinkTarget = typeof BrowserLinkTarget.Type;export const DEFAULT_BROWSER_LINK_TARGET: BrowserLinkTarget = "system"; export const LoadBalancingWeights = Schema.Record(  TrimmedNonEmptyString,  Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 100 })),); export const DiffColorScheme = Schema.Literals(["red-green", "blue-orange"]); export const ClientSettingsSchema = Schema.Struct({  diffColorScheme: DiffColorScheme.pipe(    Schema.withDecodingDefault(Effect.succeed("red-green" as const)),  ),  loadBalancingEnabled: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))),  loadBalancingWeights: LoadBalancingWeights.pipe(Schema.withDecodingDefault(Effect.succeed({}))),  appearanceContrast: AppearanceContrast.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_APPEARANCE_CONTRAST)),  ),  // Panel motion defaults to zero because width and height transitions cause  // layout work on every frame, which is noticeable on lower-power clients.  panelAnimationDurationMs: PanelAnimationDurationMs.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_PANEL_ANIMATION_DURATION_MS)),  ),  browserDefaultViewport: PreviewViewportSetting.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_BROWSER_VIEWPORT)),  ),  browserDefaultZoomFactor: PreviewZoomFactor.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_PREVIEW_ZOOM_FACTOR)),  ),  browserDefaultAppearance: PreviewAppearancePreference.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_PREVIEW_APPEARANCE)),  ),  browserRecordingFrameRate: BrowserRecordingFrameRate.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_BROWSER_RECORDING_FRAME_RATE)),  ),  /**   * Where links clicked in a thread (chat markdown, terminal output) open.   * Only the desktop app has an in-app browser, so other clients ignore "app".   */  browserLinkTarget: BrowserLinkTarget.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_BROWSER_LINK_TARGET)),  ),  /**   * Whether an agent using a preview pops the floating mini player into   * view. Only applies when the agent didn't ask either way — an explicit   * `open`/`show` on `preview_open` still wins, since that is the agent   * deliberately showing or hiding its work.   */  browserAutoShowFloatingPreview: Schema.Boolean.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_BROWSER_AUTO_SHOW_FLOATING_PREVIEW)),  ),  /**   * User-created browser profiles. The built-in Default and Incognito profiles   * are synthesized by `resolveBrowserProfiles`, not stored here, so they   * cannot be renamed away or deleted.   */  browserProfiles: Schema.Array(BrowserProfile).pipe(    Schema.withDecodingDefault(Effect.succeed([])),  ),  /** Profile new tabs open under. Falls back to Default if it no longer exists. */  browserDefaultProfileId: BrowserProfileId.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_BROWSER_PROFILE_ID)),  ),  // Desktop-only. Boolean values from older settings files decode to their  // equivalent mode and encode back as the canonical string value.  confirmQuit: QuitConfirmationModeSetting.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_QUIT_CONFIRMATION_MODE)),  ),  confirmThreadArchive: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))),  confirmThreadDelete: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),  confirmThreadUnpin: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))),  dismissedProviderUpdateNotificationKeys: Schema.Array(TrimmedNonEmptyString).pipe(    Schema.withDecodingDefault(Effect.succeed([])),  ),  diffIgnoreWhitespace: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),  diffLayout: DiffLayout.pipe(Schema.withDecodingDefault(Effect.succeed(DEFAULT_DIFF_LAYOUT))),  environmentIdentificationMode: EnvironmentIdentificationMode.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_ENVIRONMENT_IDENTIFICATION_MODE)),  ),  glassOpacity: GlassOpacity.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_GLASS_OPACITY)),  ),  fontSizeInterface: InterfaceFontSize.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_INTERFACE_FONT_SIZE)),  ),  fontSizePrompt: PromptFontSize.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_PROMPT_FONT_SIZE)),  ),  fontSizeCode: CodeFontSize.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_CODE_FONT_SIZE)),  ),  fontSizeTerminal: TerminalFontSize.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_TERMINAL_FONT_SIZE)),  ),  fontFamilyCode: FontFamilyPreference.pipe(Schema.withDecodingDefault(Effect.succeed(""))),  fontFamilyComposer: FontFamilyPreference.pipe(Schema.withDecodingDefault(Effect.succeed(""))),  fontFamilySans: FontFamilyPreference.pipe(Schema.withDecodingDefault(Effect.succeed(""))),  fontFamilyTerminal: FontFamilyPreference.pipe(Schema.withDecodingDefault(Effect.succeed(""))),  // Grayscale `-webkit-font-smoothing: antialiased` (thinner strokes);  // disabling restores the platform's heavier default. No effect off macOS.  fontSmoothing: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),  // When the first-run welcome wizard finished (or was skipped), as an ISO  // timestamp. `null` alone does not mean "show the wizard" — every install  // that predates this field decodes to `null` — so the gate also requires an  // empty workspace before it treats the client as a fresh install.  onboardingCompletedAt: Schema.NullOr(Schema.String).pipe(    Schema.withDecodingDefault(Effect.succeed(null)),  ),  // Model favorites. Historically keyed by provider kind, now  // widened to `ProviderInstanceId` so users can favorite a specific model  // on a custom provider instance (e.g. "Codex Personal · gpt-5") without  // the UI collapsing it into the same bucket as the default Codex. The  // widening is backward-compatible by construction: prior provider-kind  // strings satisfy the `ProviderInstanceId` slug schema, so previously  // persisted favorites decode unchanged and continue to point at the  // default instance for their kind (because `defaultInstanceIdForDriver(kind)`  // uses the same slug). The field name is kept as `provider` for storage  // stability; new call sites should treat the value as an instance id.  favorites: Schema.Array(    Schema.Struct({      provider: ProviderInstanceId,      model: TrimmedNonEmptyString,    }),  ).pipe(Schema.withDecodingDefault(Effect.succeed([]))),  providerModelPreferences: Schema.Record(    ProviderInstanceId,    Schema.Struct({      hiddenModels: Schema.Array(Schema.String).pipe(        Schema.withDecodingDefault(Effect.succeed([])),      ),      modelOrder: Schema.Array(Schema.String).pipe(Schema.withDecodingDefault(Effect.succeed([]))),    }),  ).pipe(Schema.withDecodingDefault(Effect.succeed({}))),  pullRequestMergeMethodOverrides: Schema.Record(    TrimmedNonEmptyString,    PullRequestMergeMethod,  ).pipe(Schema.withDecodingDefault(Effect.succeed({}))),  // Legacy plan mode. The composer's Build/Plan toggle was removed from the  // default UI; this beta flag restores it (plus the /plan and /default slash  // commands) for users who still rely on the old workflow.  planModeEnabled: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))),  // Legacy context window meter. The composer hides it by default; users who  // still want the old usage indicator can restore it from Settings.  contextWindowMeterEnabled: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))),  // Desktop resting composer: scrolling an existing thread's conversation  // settles the composer into its single-line layout. Losing focus never does.  composerCollapseOnScroll: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),  proactivePanelsEnabled: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))),  showSkillsInSlashMenu: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),  // Legacy sidebar (the original per-project tree). Deliberately a fresh key  // (was `sidebarV2Enabled` + `sidebarV2ConfiguredByUser`): decoding drops the  // old keys, so everyone, including prior beta opt-outs, resets to the new  // default sidebar.  legacySidebarEnabled: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))),  sidebarProjectGroupingMode: SidebarProjectGroupingMode.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_SIDEBAR_PROJECT_GROUPING_MODE)),  ),  sidebarProjectGroupingOverrides: Schema.Record(    TrimmedNonEmptyString,    SidebarProjectGroupingMode,  ).pipe(Schema.withDecodingDefault(Effect.succeed({}))),  sidebarProjectSortOrder: SidebarProjectSortOrder.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_SIDEBAR_PROJECT_SORT_ORDER)),  ),  sidebarThreadSortOrder: SidebarThreadSortOrder.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_SIDEBAR_THREAD_SORT_ORDER)),  ),  sidebarThreadPreviewCount: SidebarThreadPreviewCount.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_SIDEBAR_THREAD_PREVIEW_COUNT)),  ),  timestampFormat: TimestampFormat.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_TIMESTAMP_FORMAT)),  ),  snapShotEnabled: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))),  snapShotIncludeAccessibility: Schema.Boolean.pipe(    Schema.withDecodingDefault(Effect.succeed(true)),  ),  snapShotShortcut: SnapShotShortcut.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_SNAP_SHOT_SHORTCUT)),  ),  snapShotPlaySound: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),  snapShotSound: SnapShotSound.pipe(    Schema.withDecodingDefault(Effect.succeed(DEFAULT_SNAP_SHOT_SOUND)),  ),  snapShotFlash: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),  snapShotAnimations: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),  wordWrap: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),});export type ClientSettings = typeof ClientSettingsSchema.Type; export const DEFAULT_CLIENT_SETTINGS: ClientSettings = Schema.decodeSync(ClientSettingsSchema)({}); // ── Server Settings (server-authoritative) ──────────────────── const UsageModelTokenPrice = Schema.Number.check(  Schema.isFinite(),  Schema.isGreaterThanOrEqualTo(0),); /** USD per million tokens. Omitted cache rates use the input rate. */export const UsageModelPriceOverride = Schema.Struct({  inputCostPerMillionTokens: UsageModelTokenPrice,  outputCostPerMillionTokens: UsageModelTokenPrice,  cacheReadCostPerMillionTokens: Schema.optionalKey(UsageModelTokenPrice),  cacheWriteCostPerMillionTokens: Schema.optionalKey(UsageModelTokenPrice),});export type UsageModelPriceOverride = typeof UsageModelPriceOverride.Type; const makeBinaryPathSetting = (fallback: string) =>  TrimmedString.pipe(    Schema.decodeTo(      Schema.String,      SchemaTransformation.transformOrFail({        decode: (value) => Effect.succeed(value || fallback),        encode: (value) => Effect.succeed(value),      }),    ),    Schema.withDecodingDefault(Effect.succeed(fallback)),  ); export type ProviderSettingsFormControl = "text" | "password" | "textarea" | "switch" | "select"; export interface ProviderSettingsFormOption {  readonly value: string;  readonly label: string;} export interface ProviderSettingsFormAnnotation {  readonly control?: ProviderSettingsFormControl | undefined;  readonly placeholder?: string | undefined;  readonly hidden?: boolean | undefined;  readonly clearWhenEmpty?: "omit" | "persist" | undefined;  /** Choices for a `select` control. The first entry is the default. */

Lines 1–500 of 1403.