src / config.ts

/**
 * @file Merged config schematics — single chain combining tools + memory + media fields.
 *
 * IMPORTANT: The sub-namespace config exports (tools/config.ts → toolsPluginConfig,
 * memory/config.ts → configSchematics) are still used by their respective modules
 * via ctl.getPluginConfig(). Since SDK does structural key lookup at runtime,
 * all keys present here will be readable through either sub-schematics object.
 */

import { createConfigSchematics } from "@lmstudio/sdk";

export const maestroConfigSchematics = createConfigSchematics()

  // ── Tools Plugin Fields (from beledarian) ──────────────────────────

  .field("defaultWorkingDirectory", "string", {
    displayName: "🔧 Default Working Directory",
    subtitle: "Starting directory when plugin loads. Leave empty for default (~/.maestro-toolbox/workspace). Applies on next chat.",
  }, "")
  .field("retrievalLimit", "numeric", {
    int: true,
    min: 1,
    displayName: "🔧 Retrieval Limit",
    subtitle: "When retrieval is triggered, this is the maximum number of chunks to return.",
    slider: { min: 1, max: 10, step: 1 },
  }, 3)
  .field("retrievalAffinityThreshold", "numeric", {
    min: 0.0,
    max: 1.0,
    displayName: "🔧 Retrieval Affinity Threshold",
    subtitle: "The minimum similarity score for a chunk to be considered relevant.",
    slider: { min: 0.0, max: 1.0, step: 0.01 },
  }, 0.5)
  .field("allowJavascriptExecution", "boolean", {
    displayName: "⚡ Allow JavaScript Execution",
    subtitle: "Enable the 'run_javascript' tool. DANGER: Code runs on your machine.",
  }, false)
  .field("allowPythonExecution", "boolean", {
    displayName: "⚡ Allow Python Execution",
    subtitle: "Enable the 'run_python' tool. DANGER: Code runs on your machine.",
  }, false)
  .field("allowTerminalExecution", "boolean", {
    displayName: "⚡ Allow Terminal Execution",
    subtitle: "Enable the 'run_in_terminal' tool. Opens real terminal windows.",
  }, false)
  .field("allowShellCommandExecution", "boolean", {
    displayName: "⚡ Allow Shell Command Execution",
    subtitle: "Enable the 'execute_command' tool. DANGER: Commands run on your machine.",
  }, false)
  .field("allowGitOperations", "boolean", {
    displayName: "⚡ Allow Git Operations",
    subtitle: "Enable git tools (status, commit, diff, log).",
  }, true)
  .field("allowDatabaseInspection", "boolean", {
    displayName: "⚡ Allow Database Inspection",
    subtitle: "Enable 'query_database' for SQLite files.",
  }, false)
  .field("allowSystemNotifications", "boolean", {
    displayName: "⚡ Allow System Notifications",
    subtitle: "Enable the agent to send OS notifications.",
  }, true)
  .field("allowAllCode", "boolean", {
    displayName: "⚡ Allow All Code Execution",
    subtitle: "MASTER SWITCH: Overrides all other settings to enable ALL execution tools.",
  }, false)
  .field("enableWikipediaTool", "boolean", {
    displayName: "🌐 Enable Wikipedia Tool",
    subtitle: "Enable the 'wikipedia_search' tool.",
  }, true)
  .field("enableSecondaryAgent", "boolean", {
    displayName: "🤖 Enable Secondary Agent/Model",
    subtitle: "Auto-detects a second loaded model via LM Link. If only one model is loaded, it delegates to itself.",
  }, false)
  .field("secondaryAgentEndpoint", "string", {
    displayName: "🤖 Secondary Agent Endpoint",
    subtitle: "API endpoint for the secondary model. Auto-detected model is used from this endpoint.",
  }, "http://localhost:1234/v1")
  .field("subAgentProfiles", "string", {
    displayName: "🤖 Sub-Agent Profiles (JSON)",
    subtitle: 'Define available sub-agents. Format: {"coder": "You are a coding expert...", ...}',
  }, '{"summarizer": "You are a summarization expert. Summarize the content concisely.", "coder": "You are a software engineer. Write efficient and safe code."}')
  .field("subAgentFrequency", "select", {
    displayName: "🤖 Sub-Agent Frequency",
    subtitle: "Controls how often the agent is encouraged to delegate tasks to the secondary agent.",
    options: [
      { value: "when_useful", displayName: "When useful — delegate summarization, memory, research" },
      { value: "always", displayName: "Always — delegate all auxiliary tasks" },
      { value: "hard_tasks", displayName: "Hard tasks only — delegate sparingly" },
      { value: "never", displayName: "Never — disable delegation hints entirely" },
    ],
  }, "when_useful")
  .field("subAgentAllowFileSystem", "boolean", {
    displayName: "🤖 Sub-Agent: Allow File System",
    subtitle: "If enabled, sub-agents can read/list files.",
  }, true)
  .field("subAgentAllowWeb", "boolean", {
    displayName: "🤖 Sub-Agent: Allow Web Search",
    subtitle: "If enabled, sub-agents can use Wikipedia and DuckDuckGo.",
  }, true)
  .field("subAgentAllowCode", "boolean", {
    displayName: "🤖 Sub-Agent: Allow Code Execution",
    subtitle: "If enabled, sub-agents can run Python/JS code. DANGER!",
  }, false)
  .field("enableDebugMode", "boolean", {
    displayName: "🤖 Enable Auto-Debug Mode",
    subtitle: "If enabled, coding tasks delegated to sub-agents will automatically trigger a second 'Reviewer' pass to check for errors.",
  }, false)
  .field("subAgentAutoSave", "boolean", {
    displayName: "🤖 Sub-Agent: Auto-Save Code",
    subtitle: "If enabled, code blocks generated by the sub-agent that aren't explicitly saved will be automatically saved to files.",
  }, true)
  .field("showFullCodeOutput", "boolean", {
    displayName: "🤖 Show Full Code Output",
    subtitle: "If enabled, the Main Agent will display the full code content of generated files instead of just the file paths.",
  }, false)

  // ── Persistent Memory Fields (from dirty-data) ─────────────────────

  .field("autoInjectMemories", "select", {
    displayName: "🧠 Auto-Inject Memories",
    subtitle: "Automatically add relevant memories to every conversation (via prompt preprocessor)",
    options: [
      { value: "on", displayName: "On — inject relevant memories automatically (recommended)" },
      { value: "off", displayName: "Off — only recall when explicitly asked via tools" },
    ],
  }, "on")
  .field("contextMemoryCount", "numeric", {
    displayName: "🧠 Context Memory Count",
    subtitle: "How many memories to inject per message when auto-inject is on (1–15)",
    min: 1,
    max: 15,
    int: true,
    slider: { step: 1, min: 1, max: 15 },
  }, 5)
  .field("enableAIExtraction", "select", {
    displayName: "🧠 AI Fact Extraction",
    subtitle: "Use the loaded model to automatically extract facts from conversations",
    options: [
      { value: "on", displayName: "On — AI extracts facts from your messages" },
      { value: "off", displayName: "Off — only store what you explicitly tell it to remember" },
    ],
  }, "on")
  .field("enableConflictDetection", "select", {
    displayName: "🧠 Conflict Detection",
    subtitle: "Use AI to detect contradictions between new and existing memories",
    options: [
      { value: "on", displayName: "On — detect and handle conflicting memories" },
      { value: "off", displayName: "Off — store everything without checking" },
    ],
  }, "on")
  .field("decayHalfLifeDays", "numeric", {
    displayName: "🧠 Memory Decay Half-Life (days)",
    subtitle: "How many days until an un-accessed memory loses half its retrieval priority (7–365)",
    min: 7,
    max: 365,
    int: true,
    slider: { step: 7, min: 7, max: 365 },
  }, 30)
  .field("memoryStoragePath", "string", {
    displayName: "🧠 Storage Path (advanced)",
    subtitle: "Custom directory for the memory database. Leave empty for default (~/.lmstudio/plugin-data/persistent-memory/)",
  }, "")
  .field("activeProject", "string", {
    displayName: "🧠 Active Project",
    subtitle: "Stable project slug for project-scoped retrieval and writes. When set, project memories for this project are auto-injected and AI-extracted durable facts inherit this project.",
  }, "")
  .field("memoryInjectionCategories", "string", {
    displayName: "🧠 Injection Category Filter",
    subtitle: "Comma-separated list of categories to auto-inject (e.g. 'preference,fact,project'). Leave empty for all categories.",
  }, "")

  // ── Visual/Design Fields ──────────────────────────────────
  .field("enableDesignMode", "boolean", {
    displayName: "🎨 Enable Design Mode",
    subtitle: "Injects design knowledge + 58 real design systems (Apple, Figma, Notion, etc.) the model can load on demand.",
  }, false)

  // ── Media Tools Fields (new) ──────────────────────────────────

  .field("enableImageAnalysis", "boolean", {
    displayName: "📷 Enable Image Analysis",
    subtitle: "Enable the analyze_image tool to describe images from local paths.",
  }, true)
  .field("imageMaxDimension", "numeric", {
    displayName: "📷 Image Max Dimension (px)",
    subtitle: "Maximum width or height for image analysis. Lower = less tokens, faster.",
    int: true,
    min: 128,
    max: 1280,
    slider: { min: 128, max: 1280, step: 64 },
  }, 512)
  .field("enableVideoAnalysis", "boolean", {
    displayName: "🎬 Enable Video Analysis",
    subtitle: "Enable the analyze_video tool to extract and analyze frames from local video files. Requires ffmpeg.",
  }, true)
  .field("videoFrameCount", "numeric", {
    displayName: "🎬 Video Frame Count",
    subtitle: "Number of frames to extract for video analysis (1–10).",
    int: true,
    min: 1,
    max: 10,
    slider: { min: 1, max: 10, step: 1 },
  }, 4)
  .field("videoFrameMaxDimension", "numeric", {
    displayName: "🎬 Video Frame Max Dimension (px)",
    subtitle: "Maximum width or height of extracted video frames. Lower = faster analysis.",
    int: true,
    min: 128,
    max: 1280,
    slider: { min: 128, max: 1280, step: 64 },
  }, 384)

  .build();