import { z } from "zod" import { AnyMcpNameSchema, McpNameSchema } from "../mcp/types" const PermissionValue = z.enum(["ask", "allow", "deny"]) const BashPermission = z.union([ PermissionValue, z.record(z.string(), PermissionValue), ]) const AgentPermissionSchema = z.object({ edit: PermissionValue.optional(), bash: BashPermission.optional(), webfetch: PermissionValue.optional(), task: PermissionValue.optional(), doom_loop: PermissionValue.optional(), external_directory: PermissionValue.optional(), }) export const BuiltinAgentNameSchema = z.enum([ "sisyphus", "hephaestus", "prometheus", "oracle", "librarian", "explore", "multimodal-looker", "metis", "momus", "atlas", ]) export const BuiltinSkillNameSchema = z.enum([ "playwright", "agent-browser", "dev-browser", "frontend-ui-ux", "git-master", ]) export const OverridableAgentNameSchema = z.enum([ "build", "plan", "sisyphus", "hephaestus", "sisyphus-junior", "OpenCode-Builder", "prometheus", "metis", "momus", "oracle", "librarian", "explore", "multimodal-looker", "atlas", ]) export const AgentNameSchema = BuiltinAgentNameSchema export const HookNameSchema = z.enum([ "todo-continuation-enforcer", "context-window-monitor", "session-recovery", "session-notification", "comment-checker", "grep-output-truncator", "tool-output-truncator", "question-label-truncator", "directory-agents-injector", "directory-readme-injector", "empty-task-response-detector", "think-mode", "subagent-question-blocker", "anthropic-context-window-limit-recovery", "preemptive-compaction", "rules-injector", "background-notification", "auto-update-checker", "startup-toast", "keyword-detector", "agent-usage-reminder", "non-interactive-env", "interactive-bash-session", "thinking-block-validator", "ralph-loop", "category-skill-reminder", "compaction-context-injector", "compaction-todo-preserver", "claude-code-hooks", "auto-slash-command", "edit-error-recovery", "delegate-task-retry", "prometheus-md-only", "sisyphus-junior-notepad", "start-work", "atlas", "unstable-agent-babysitter", "task-reminder", "task-resume-info", "stop-continuation-guard", "tasks-todowrite-disabler", "write-existing-file-guard", "anthropic-effort", ]) export const BuiltinCommandNameSchema = z.enum([ "init-deep", "ralph-loop", "ulw-loop", "cancel-ralph", "refactor", "start-work", "stop-continuation", ]) export const AgentOverrideConfigSchema = z.object({ /** @deprecated Use `category` instead. Model is inherited from category defaults. */ model: z.string().optional(), variant: z.string().optional(), /** Category name to inherit model and other settings from CategoryConfig */ category: z.string().optional(), /** Skill names to inject into agent prompt */ skills: z.array(z.string()).optional(), temperature: z.number().min(0).max(2).optional(), top_p: z.number().min(0).max(1).optional(), prompt: z.string().optional(), prompt_append: z.string().optional(), tools: z.record(z.string(), z.boolean()).optional(), disable: z.boolean().optional(), description: z.string().optional(), mode: z.enum(["subagent", "primary", "all"]).optional(), color: z .string() .regex(/^#[0-9A-Fa-f]{6}$/) .optional(), permission: AgentPermissionSchema.optional(), /** Maximum tokens for response. Passed directly to OpenCode SDK. */ maxTokens: z.number().optional(), /** Extended thinking configuration (Anthropic). Overrides category and default settings. */ thinking: z.object({ type: z.enum(["enabled", "disabled"]), budgetTokens: z.number().optional(), }).optional(), /** Reasoning effort level (OpenAI). Overrides category and default settings. */ reasoningEffort: z.enum(["low", "medium", "high", "xhigh"]).optional(), /** Text verbosity level. */ textVerbosity: z.enum(["low", "medium", "high"]).optional(), /** Provider-specific options. Passed directly to OpenCode SDK. */ providerOptions: z.record(z.string(), z.unknown()).optional(), }) export const AgentOverridesSchema = z.object({ build: AgentOverrideConfigSchema.optional(), plan: AgentOverrideConfigSchema.optional(), sisyphus: AgentOverrideConfigSchema.optional(), hephaestus: AgentOverrideConfigSchema.optional(), "sisyphus-junior": AgentOverrideConfigSchema.optional(), "OpenCode-Builder": AgentOverrideConfigSchema.optional(), prometheus: AgentOverrideConfigSchema.optional(), metis: AgentOverrideConfigSchema.optional(), momus: AgentOverrideConfigSchema.optional(), oracle: AgentOverrideConfigSchema.optional(), librarian: AgentOverrideConfigSchema.optional(), explore: AgentOverrideConfigSchema.optional(), "multimodal-looker": AgentOverrideConfigSchema.optional(), atlas: AgentOverrideConfigSchema.optional(), }) export const ClaudeCodeConfigSchema = z.object({ mcp: z.boolean().optional(), commands: z.boolean().optional(), skills: z.boolean().optional(), agents: z.boolean().optional(), hooks: z.boolean().optional(), plugins: z.boolean().optional(), plugins_override: z.record(z.string(), z.boolean()).optional(), }) export const SisyphusAgentConfigSchema = z.object({ disabled: z.boolean().optional(), default_builder_enabled: z.boolean().optional(), planner_enabled: z.boolean().optional(), replace_plan: z.boolean().optional(), }) export const CategoryConfigSchema = z.object({ /** Human-readable description of the category's purpose. Shown in task prompt. */ description: z.string().optional(), model: z.string().optional(), variant: z.string().optional(), temperature: z.number().min(0).max(2).optional(), top_p: z.number().min(0).max(1).optional(), maxTokens: z.number().optional(), thinking: z.object({ type: z.enum(["enabled", "disabled"]), budgetTokens: z.number().optional(), }).optional(), reasoningEffort: z.enum(["low", "medium", "high", "xhigh"]).optional(), textVerbosity: z.enum(["low", "medium", "high"]).optional(), tools: z.record(z.string(), z.boolean()).optional(), prompt_append: z.string().optional(), /** Mark agent as unstable - forces background mode for monitoring. Auto-enabled for gemini/minimax models. */ is_unstable_agent: z.boolean().optional(), }) export const BuiltinCategoryNameSchema = z.enum([ "visual-engineering", "ultrabrain", "deep", "artistry", "quick", "unspecified-low", "unspecified-high", "writing", ]) export const CategoriesConfigSchema = z.record(z.string(), CategoryConfigSchema) export const CommentCheckerConfigSchema = z.object({ /** Custom prompt to replace the default warning message. Use {{comments}} placeholder for detected comments XML. */ custom_prompt: z.string().optional(), }) export const DynamicContextPruningConfigSchema = z.object({ /** Enable dynamic context pruning (default: false) */ enabled: z.boolean().default(false), /** Notification level: off, minimal, or detailed (default: detailed) */ notification: z.enum(["off", "minimal", "detailed"]).default("detailed"), /** Turn protection - prevent pruning recent tool outputs */ turn_protection: z.object({ enabled: z.boolean().default(true), turns: z.number().min(1).max(10).default(3), }).optional(), /** Tools that should never be pruned */ protected_tools: z.array(z.string()).default([ "task", "todowrite", "todoread", "lsp_rename", "session_read", "session_write", "session_search", ]), /** Pruning strategies configuration */ strategies: z.object({ /** Remove duplicate tool calls (same tool + same args) */ deduplication: z.object({ enabled: z.boolean().default(true), }).optional(), /** Prune write inputs when file subsequently read */ supersede_writes: z.object({ enabled: z.boolean().default(true), /** Aggressive mode: prune any write if ANY subsequent read */ aggressive: z.boolean().default(false), }).optional(), /** Prune errored tool inputs after N turns */ purge_errors: z.object({ enabled: z.boolean().default(true), turns: z.number().min(1).max(20).default(5), }).optional(), }).optional(), }) export const ExperimentalConfigSchema = z.object({ aggressive_truncation: z.boolean().optional(), auto_resume: z.boolean().optional(), preemptive_compaction: z.boolean().optional(), /** Truncate all tool outputs, not just whitelisted tools (default: false). Tool output truncator is enabled by default - disable via disabled_hooks. */ truncate_all_tool_outputs: z.boolean().optional(), /** Dynamic context pruning configuration */ dynamic_context_pruning: DynamicContextPruningConfigSchema.optional(), /** Enable experimental task system for Todowrite disabler hook */ task_system: z.boolean().optional(), /** Timeout in ms for loadAllPluginComponents during config handler init (default: 10000, min: 1000) */ plugin_load_timeout_ms: z.number().min(1000).optional(), /** Wrap hook creation in try/catch to prevent one failing hook from crashing the plugin (default: true at call site) */ safe_hook_creation: z.boolean().optional(), }) export const SkillSourceSchema = z.union([ z.string(), z.object({ path: z.string(), recursive: z.boolean().optional(), glob: z.string().optional(), }), ]) export const SkillDefinitionSchema = z.object({ description: z.string().optional(), template: z.string().optional(), from: z.string().optional(), model: z.string().optional(), agent: z.string().optional(), subtask: z.boolean().optional(), "argument-hint": z.string().optional(), license: z.string().optional(), compatibility: z.string().optional(), metadata: z.record(z.string(), z.unknown()).optional(), "allowed-tools": z.array(z.string()).optional(), disable: z.boolean().optional(), }) export const SkillEntrySchema = z.union([ z.boolean(), SkillDefinitionSchema, ]) export const SkillsConfigSchema = z.union([ z.array(z.string()), z.record(z.string(), SkillEntrySchema).and(z.object({ sources: z.array(SkillSourceSchema).optional(), enable: z.array(z.string()).optional(), disable: z.array(z.string()).optional(), }).partial()), ]) export const RalphLoopConfigSchema = z.object({ /** Enable ralph loop functionality (default: false - opt-in feature) */ enabled: z.boolean().default(false), /** Default max iterations if not specified in command (default: 100) */ default_max_iterations: z.number().min(1).max(1000).default(100), /** Custom state file directory relative to project root (default: .opencode/) */ state_dir: z.string().optional(), }) export const TodoContinuationConfigSchema = z.object({ /** Max continuation injections per session before stopping (default: 8) */ max_injections: z.number().min(1).max(1000).default(8), /** Max consecutive injections with no todo progress before stopping (default: 3) */ max_stale_injections: z.number().min(0).max(1000).default(3), }) export const BackgroundTaskConfigSchema = z.object({ defaultConcurrency: z.number().min(1).optional(), providerConcurrency: z.record(z.string(), z.number().min(0)).optional(), modelConcurrency: z.record(z.string(), z.number().min(0)).optional(), /** Stale timeout in milliseconds - interrupt tasks with no activity for this duration (default: 180000 = 3 minutes, minimum: 60000 = 1 minute) */ staleTimeoutMs: z.number().min(60000).optional(), }) export const NotificationConfigSchema = z.object({ /** Force enable session-notification even if external notification plugins are detected (default: false) */ force_enable: z.boolean().optional(), }) export const BabysittingConfigSchema = z.object({ timeout_ms: z.number().default(120000), }) export const GitMasterConfigSchema = z.object({ /** Add "Ultraworked with Sisyphus" footer to commit messages (default: true). Can be boolean or custom string. */ commit_footer: z.union([z.boolean(), z.string()]).default(true), /** Add "Co-authored-by: Sisyphus" trailer to commit messages (default: true) */ include_co_authored_by: z.boolean().default(true), }) export const BrowserAutomationProviderSchema = z.enum(["playwright", "agent-browser", "dev-browser"]) export const BrowserAutomationConfigSchema = z.object({ /** * Browser automation provider to use for the "playwright" skill. * - "playwright": Uses Playwright MCP server (@playwright/mcp) - default * - "agent-browser": Uses Vercel's agent-browser CLI (requires: bun add -g agent-browser) * - "dev-browser": Uses dev-browser skill with persistent browser state */ provider: BrowserAutomationProviderSchema.default("playwright"), }) export const WebsearchProviderSchema = z.enum(["exa", "tavily"]) export const WebsearchConfigSchema = z.object({ /** * Websearch provider to use. * - "exa": Uses Exa websearch (default, works without API key) * - "tavily": Uses Tavily websearch (requires TAVILY_API_KEY) */ provider: WebsearchProviderSchema.optional(), }) export const TmuxLayoutSchema = z.enum([ 'main-horizontal', // main pane top, agent panes bottom stack 'main-vertical', // main pane left, agent panes right stack (default) 'tiled', // all panes same size grid 'even-horizontal', // all panes horizontal row 'even-vertical', // all panes vertical stack ]) export const TmuxConfigSchema = z.object({ enabled: z.boolean().default(false), layout: TmuxLayoutSchema.default('main-vertical'), main_pane_size: z.number().min(20).max(80).default(60), main_pane_min_width: z.number().min(40).default(120), agent_pane_min_width: z.number().min(20).default(40), }) export const SisyphusTasksConfigSchema = z.object({ /** Absolute or relative storage path override. When set, bypasses global config dir. */ storage_path: z.string().optional(), /** Force task list ID (alternative to env ULTRAWORK_TASK_LIST_ID) */ task_list_id: z.string().optional(), /** Enable Claude Code path compatibility mode */ claude_code_compat: z.boolean().default(false), }) export const SisyphusConfigSchema = z.object({ tasks: SisyphusTasksConfigSchema.optional(), }) export const OhMyOpenCodeConfigSchema = z.object({ $schema: z.string().optional(), /** Enable new task system (default: false) */ new_task_system_enabled: z.boolean().optional(), /** Default agent name for `oh-my-opencode run` (env: OPENCODE_DEFAULT_AGENT) */ default_run_agent: z.string().optional(), disabled_mcps: z.array(AnyMcpNameSchema).optional(), disabled_agents: z.array(BuiltinAgentNameSchema).optional(), disabled_skills: z.array(BuiltinSkillNameSchema).optional(), disabled_hooks: z.array(HookNameSchema).optional(), disabled_commands: z.array(BuiltinCommandNameSchema).optional(), /** Disable specific tools by name (e.g., ["todowrite", "todoread"]) */ disabled_tools: z.array(z.string()).optional(), agents: AgentOverridesSchema.optional(), categories: CategoriesConfigSchema.optional(), claude_code: ClaudeCodeConfigSchema.optional(), sisyphus_agent: SisyphusAgentConfigSchema.optional(), comment_checker: CommentCheckerConfigSchema.optional(), experimental: ExperimentalConfigSchema.optional(), auto_update: z.boolean().optional(), skills: SkillsConfigSchema.optional(), ralph_loop: RalphLoopConfigSchema.optional(), todo_continuation: TodoContinuationConfigSchema.optional(), background_task: BackgroundTaskConfigSchema.optional(), notification: NotificationConfigSchema.optional(), babysitting: BabysittingConfigSchema.optional(), git_master: GitMasterConfigSchema.optional(), browser_automation_engine: BrowserAutomationConfigSchema.optional(), websearch: WebsearchConfigSchema.optional(), tmux: TmuxConfigSchema.optional(), sisyphus: SisyphusConfigSchema.optional(), /** Migration history to prevent re-applying migrations (e.g., model version upgrades) */ _migrations: z.array(z.string()).optional(), }) export type OhMyOpenCodeConfig = z.infer export type AgentOverrideConfig = z.infer export type AgentOverrides = z.infer export type BackgroundTaskConfig = z.infer export type AgentName = z.infer export type HookName = z.infer export type BuiltinCommandName = z.infer export type BuiltinSkillName = z.infer export type SisyphusAgentConfig = z.infer export type CommentCheckerConfig = z.infer export type ExperimentalConfig = z.infer export type DynamicContextPruningConfig = z.infer export type SkillsConfig = z.infer export type SkillDefinition = z.infer export type RalphLoopConfig = z.infer export type TodoContinuationConfig = z.infer export type NotificationConfig = z.infer export type BabysittingConfig = z.infer export type CategoryConfig = z.infer export type CategoriesConfig = z.infer export type BuiltinCategoryName = z.infer export type GitMasterConfig = z.infer export type BrowserAutomationProvider = z.infer export type BrowserAutomationConfig = z.infer export type WebsearchProvider = z.infer export type WebsearchConfig = z.infer export type TmuxConfig = z.infer export type TmuxLayout = z.infer export type SisyphusTasksConfig = z.infer export type SisyphusConfig = z.infer export { AnyMcpNameSchema, type AnyMcpName, McpNameSchema, type McpName } from "../mcp/types"