import { z } from "zod"; export const SecretSchema = z.object({ value: z.string(), encryptionType: z.enum(["electron-safe-storage", "plaintext"]).optional(), }); export type Secret = z.infer; /** * Zod schema for chat summary objects returned by the get-chats IPC */ export const ChatSummarySchema = z.object({ id: z.number(), appId: z.number(), title: z.string().nullable(), createdAt: z.date(), }); /** * Type derived from the ChatSummarySchema */ export type ChatSummary = z.infer; /** * Zod schema for an array of chat summaries */ export const ChatSummariesSchema = z.array(ChatSummarySchema); /** * Zod schema for chat search result objects returned by the search-chats IPC */ export const ChatSearchResultSchema = z.object({ id: z.number(), appId: z.number(), title: z.string().nullable(), createdAt: z.date(), matchedMessageContent: z.string().nullable(), }); /** * Type derived from the ChatSearchResultSchema */ export type ChatSearchResult = z.infer; export const ChatSearchResultsSchema = z.array(ChatSearchResultSchema); // Zod schema for app search result objects returned by the search-app IPC export const AppSearchResultSchema = z.object({ id: z.number(), name: z.string(), createdAt: z.date(), matchedChatTitle: z.string().nullable(), matchedChatMessage: z.string().nullable(), }); // Type derived from AppSearchResultSchema export type AppSearchResult = z.infer; export const AppSearchResultsSchema = z.array(AppSearchResultSchema); const providers = [ "openai", "anthropic", "google", "vertex", "auto", "openrouter", "ollama", "lmstudio", "azure", "xai", "bedrock", ] as const; export const cloudProviders = providers.filter( (provider) => provider !== "ollama" && provider !== "lmstudio", ); /** * Zod schema for large language model configuration */ export const LargeLanguageModelSchema = z.object({ name: z.string(), provider: z.string(), customModelId: z.number().optional(), }); /** * Type derived from the LargeLanguageModelSchema */ export type LargeLanguageModel = z.infer; /** * Zod schema for provider settings * Regular providers use only apiKey. Vertex has additional optional fields. */ export const RegularProviderSettingSchema = z.object({ apiKey: SecretSchema.optional(), }); export const AzureProviderSettingSchema = z.object({ apiKey: SecretSchema.optional(), resourceName: z.string().optional(), }); export const VertexProviderSettingSchema = z.object({ // We make this undefined so that it makes existing callsites easier. apiKey: z.undefined(), projectId: z.string().optional(), location: z.string().optional(), serviceAccountKey: SecretSchema.optional(), }); export const ProviderSettingSchema = z.union([ // Must use more specific type first! // Zod uses the first type that matches. // // We use passthrough as a hack because Azure and Vertex // will match together since their required fields overlap. // // In addition, there may be future provider settings that // we may want to preserve (e.g. user downgrades to older version) // so doing passthrough keeps these extra fields. AzureProviderSettingSchema.passthrough(), VertexProviderSettingSchema.passthrough(), RegularProviderSettingSchema.passthrough(), ]); /** * Type derived from the ProviderSettingSchema */ export type ProviderSetting = z.infer; export type RegularProviderSetting = z.infer< typeof RegularProviderSettingSchema >; export type AzureProviderSetting = z.infer; export type VertexProviderSetting = z.infer; export const RuntimeModeSchema = z.enum(["web-sandbox", "local-node", "unset"]); export type RuntimeMode = z.infer; export const RuntimeMode2Schema = z.enum(["host", "docker"]); export type RuntimeMode2 = z.infer; export const ChatModeSchema = z.enum(["build", "ask", "agent"]); export type ChatMode = z.infer; export const GitHubSecretsSchema = z.object({ accessToken: SecretSchema.nullable(), }); export type GitHubSecrets = z.infer; export const GithubUserSchema = z.object({ email: z.string(), }); export type GithubUser = z.infer; export const SupabaseSchema = z.object({ accessToken: SecretSchema.optional(), refreshToken: SecretSchema.optional(), expiresIn: z.number().optional(), tokenTimestamp: z.number().optional(), }); export type Supabase = z.infer; export const NeonSchema = z.object({ accessToken: SecretSchema.optional(), refreshToken: SecretSchema.optional(), expiresIn: z.number().optional(), tokenTimestamp: z.number().optional(), }); export type Neon = z.infer; export const ExperimentsSchema = z.object({ // Deprecated enableSupabaseIntegration: z.boolean().describe("DEPRECATED").optional(), enableFileEditing: z.boolean().describe("DEPRECATED").optional(), }); export type Experiments = z.infer; export const DyadProBudgetSchema = z.object({ budgetResetAt: z.string(), maxBudget: z.number(), }); export type DyadProBudget = z.infer; export const GlobPathSchema = z.object({ globPath: z.string(), }); export type GlobPath = z.infer; export const AppChatContextSchema = z.object({ contextPaths: z.array(GlobPathSchema), smartContextAutoIncludes: z.array(GlobPathSchema), excludePaths: z.array(GlobPathSchema).optional(), }); export type AppChatContext = z.infer; export type ContextPathResult = GlobPath & { files: number; tokens: number; }; export type ContextPathResults = { contextPaths: ContextPathResult[]; smartContextAutoIncludes: ContextPathResult[]; excludePaths: ContextPathResult[]; }; export const ReleaseChannelSchema = z.enum(["stable", "beta"]); export type ReleaseChannel = z.infer; /** * Zod schema for user settings */ export const UserSettingsSchema = z.object({ selectedModel: LargeLanguageModelSchema, providerSettings: z.record(z.string(), ProviderSettingSchema), githubUser: GithubUserSchema.optional(), githubAccessToken: SecretSchema.optional(), vercelAccessToken: SecretSchema.optional(), supabase: SupabaseSchema.optional(), neon: NeonSchema.optional(), autoApproveChanges: z.boolean().optional(), telemetryConsent: z.enum(["opted_in", "opted_out", "unset"]).optional(), telemetryUserId: z.string().optional(), hasRunBefore: z.boolean().optional(), enableDyadPro: z.boolean().optional(), experiments: ExperimentsSchema.optional(), lastShownReleaseNotesVersion: z.string().optional(), maxChatTurnsInContext: z.number().optional(), thinkingBudget: z.enum(["low", "medium", "high"]).optional(), enableProLazyEditsMode: z.boolean().optional(), proLazyEditsMode: z.enum(["off", "v1", "v2"]).optional(), enableProSmartFilesContextMode: z.boolean().optional(), enableProWebSearch: z.boolean().optional(), proSmartContextOption: z.enum(["balanced", "conservative"]).optional(), selectedTemplateId: z.string(), enableSupabaseWriteSqlMigration: z.boolean().optional(), selectedChatMode: ChatModeSchema.optional(), acceptedCommunityCode: z.boolean().optional(), enableAutoFixProblems: z.boolean().optional(), enableNativeGit: z.boolean().optional(), enableAutoUpdate: z.boolean(), releaseChannel: ReleaseChannelSchema, runtimeMode2: RuntimeMode2Schema.optional(), customNodePath: z.string().optional().nullable(), //////////////////////////////// // E2E TESTING ONLY. //////////////////////////////// isTestMode: z.boolean().optional(), //////////////////////////////// // DEPRECATED. //////////////////////////////// enableProSaverMode: z.boolean().optional(), dyadProBudget: DyadProBudgetSchema.optional(), runtimeMode: RuntimeModeSchema.optional(), }); /** * Type derived from the UserSettingsSchema */ export type UserSettings = z.infer; export function isDyadProEnabled(settings: UserSettings): boolean { return settings.enableDyadPro === true && hasDyadProKey(settings); } export function hasDyadProKey(settings: UserSettings): boolean { return !!settings.providerSettings?.auto?.apiKey?.value; } export function isTurboEditsV2Enabled(settings: UserSettings): boolean { return Boolean( isDyadProEnabled(settings) && settings.enableProLazyEditsMode === true && settings.proLazyEditsMode === "v2", ); } // Define interfaces for the props export interface SecurityRisk { type: "warning" | "danger"; title: string; description: string; } export interface FileChange { name: string; path: string; summary: string; type: "write" | "rename" | "delete"; isServerFunction: boolean; } export interface CodeProposal { type: "code-proposal"; title: string; securityRisks: SecurityRisk[]; filesChanged: FileChange[]; packagesAdded: string[]; sqlQueries: SqlQuery[]; } export type SuggestedAction = | RestartAppAction | SummarizeInNewChatAction | RefactorFileAction | WriteCodeProperlyAction | RebuildAction | RestartAction | RefreshAction | KeepGoingAction; export interface RestartAppAction { id: "restart-app"; } export interface SummarizeInNewChatAction { id: "summarize-in-new-chat"; } export interface WriteCodeProperlyAction { id: "write-code-properly"; } export interface RefactorFileAction { id: "refactor-file"; path: string; } export interface RebuildAction { id: "rebuild"; } export interface RestartAction { id: "restart"; } export interface RefreshAction { id: "refresh"; } export interface KeepGoingAction { id: "keep-going"; } export interface ActionProposal { type: "action-proposal"; actions: SuggestedAction[]; } export interface TipProposal { type: "tip-proposal"; title: string; description: string; } export type Proposal = CodeProposal | ActionProposal | TipProposal; export interface ProposalResult { proposal: Proposal; chatId: number; messageId: number; } export interface SqlQuery { content: string; description?: string; }