{
  "name": "astra",
  "displayName": "Astra",
  "description": "The personal intelligence layer for Antigravity and VS Code. A private cognitive partner for deep project context, memory, and proactive strategic decision-making.",
  "version": "2.2.210",
  "publisher": "g1nation",
  "license": "MIT",
  "icon": "assets/icon.png",
  "repository": {
    "type": "git",
    "url": "https://github.com/g1nations/locallm"
  },
  "engines": {
    "vscode": "^1.80.0"
  },
  "categories": [
    "Machine Learning",
    "Programming Languages",
    "Chat"
  ],
  "keywords": [
    "ai",
    "local",
    "ollama",
    "gemma",
    "llama",
    "deepseek",
    "offline",
    "agent",
    "code-generation",
    "astra",
    "copilot"
  ],
  "activationEvents": [
    "onStartupFinished"
  ],
  "main": "./out/extension.js",
  "contributes": {
    "commands": [
      {
        "command": "g1nation.newChat",
        "title": "Astra: New Chat",
        "icon": "$(add)"
      },
      {
        "command": "g1nation.eval.retrieval",
        "title": "Astra: 검색 평가 실행 (recall@k / MRR)"
      },
      {
        "command": "g1nation.embeddings.backfill",
        "title": "Astra: 두뇌 임베딩 전체 색인"
      },
      {
        "command": "g1nation.exportChat",
        "title": "Astra: Export Chat as Markdown"
      },
      {
        "command": "g1nation.explainSelection",
        "title": "Astra: Explain Selected Code"
      },
      {
        "command": "g1nation.focusChat",
        "title": "Astra: Focus Chat Input"
      },
      {
        "command": "g1nation.showBrainNetwork",
        "title": "Astra: Show Brain Topology"
      },
      {
        "command": "g1nation.approval.focus",
        "title": "Astra: Focus Approval Panel"
      },
      {
        "command": "g1nation.scaffoldProject",
        "title": "Astra: Scaffold New Project"
      },
      {
        "command": "g1nation.telegram.setBotToken",
        "title": "Astra: Set Telegram Bot Token"
      },
      {
        "command": "g1nation.telegram.clearBotToken",
        "title": "Astra: Clear Telegram Bot Token"
      },
      {
        "command": "g1nation.telegram.testConnection",
        "title": "Astra: Test Telegram Connection"
      },
      {
        "command": "g1nation.settings.focus",
        "title": "Astra: Open Settings Panel"
      },
      {
        "command": "g1nation.skills.editKnowledgeMap",
        "title": "Astra: Edit Agent ↔ Knowledge Map"
      },
      {
        "command": "g1nation.openChat",
        "title": "Astra: Open Chat (Editor Column)",
        "icon": "$(comment-discussion)"
      },
      {
        "command": "g1nation.setupDatacollect",
        "title": "Astra: Setup Datacollect Dependencies (yt-dlp, youtube-transcript-api)",
        "icon": "$(cloud-download)"
      },
      {
        "command": "g1nation.lesson.create",
        "title": "Astra: New Lesson (Experience Memory)"
      },
      {
        "command": "g1nation.lesson.fromConversation",
        "title": "Astra: New Lesson from Current Conversation"
      },
      {
        "command": "g1nation.lesson.manage",
        "title": "Astra: Browse / Manage Lessons"
      },
      {
        "command": "g1nation.architecture.refresh",
        "title": "Astra: Refresh Project Architecture Context"
      },
      {
        "command": "g1nation.architecture.detach",
        "title": "Astra: Detach Project Architecture Context"
      },
      {
        "command": "g1nation.architecture.attach",
        "title": "Astra: Attach Project Architecture Context"
      },
      {
        "command": "g1nation.architecture.open",
        "title": "Astra: Open Project Architecture Doc"
      },
      {
        "command": "g1nation.company.toggle",
        "title": "Astra: Toggle 1인 기업 Mode"
      },
      {
        "command": "g1nation.company.manage",
        "title": "Astra: Manage 1인 기업 Agents"
      },
      {
        "command": "g1nation.company.openSessions",
        "title": "Astra: Open 1인 기업 Sessions Folder"
      },
      {
        "command": "g1nation.company.pixelOffice.open",
        "title": "Astra: Open Pixel Office (Full Screen)"
      },
      {
        "command": "g1nation.calendar.connect",
        "title": "Astra: Google Calendar (iCal) 연결 📅"
      },
      {
        "command": "g1nation.calendar.refresh",
        "title": "Astra: Google Calendar 새로고침 📅"
      },
      {
        "command": "g1nation.calendar.connectOAuth",
        "title": "Astra: Google Calendar OAuth 연결 (쓰기) 🔐"
      },
      {
        "command": "g1nation.devilAgent.toggle",
        "title": "Astra: Toggle Devil Agent 🎭"
      }
    ],
    "keybindings": [
      {
        "command": "g1nation.focusChat",
        "key": "cmd+l",
        "mac": "cmd+l"
      }
    ],
    "menus": {
      "editor/context": [
        {
          "command": "g1nation.explainSelection",
          "when": "editorHasSelection",
          "group": "1_modification"
        }
      ]
    },
    "viewsContainers": {
      "activitybar": [
        {
          "id": "g1nation-astra-activity",
          "title": "Astra (g1nation)",
          "icon": "assets/icon-activitybar.svg"
        }
      ]
    },
    "views": {
      "g1nation-astra-activity": [
        {
          "id": "g1nation-astra-launcher",
          "name": "Astra (g1nation) Launcher"
        }
      ]
    },
    "viewsWelcome": [
      {
        "view": "g1nation-astra-launcher",
        "contents": "✦ **Astra** — 로컬 AI 인텔리전스 레이어\n\nChat 탭을 닫았을 때 여기서 다시 열 수 있습니다.\n\n[$(comment-discussion) Open Chat](command:g1nation.openChat)\n[$(add) New Chat](command:g1nation.newChat)\n[$(gear) Settings](command:g1nation.settings.focus)\n\n---\n\n**1인 기업 모드**\n\n[$(organization) Manage Agents](command:g1nation.company.manage)\n[$(folder-opened) Open Sessions Folder](command:g1nation.company.openSessions)\n\n---\n\n**Project Architecture**\n\n[$(file-text) Open Architecture Doc](command:g1nation.architecture.open)\n[$(refresh) Refresh Architecture](command:g1nation.architecture.refresh)\n\n---\n\n**Lessons / Knowledge**\n\n[$(lightbulb) Manage Lessons](command:g1nation.lesson.manage)\n[$(edit) Edit Agent ↔ Knowledge Map](command:g1nation.skills.editKnowledgeMap)"
      }
    ],
    "configuration": {
      "title": "Astra",
      "properties": {
        "g1nation.multiAgentEnabled": {
          "type": "boolean",
          "default": false,
          "description": "Enable Multi-Agent Workflow (Planner -> Researcher -> Writer) for complex tasks."
        },
        "g1nation.datacollectBridgeTarget": {
          "type": "string",
          "enum": [
            "local",
            "nas"
          ],
          "default": "local",
          "markdownDescription": "Datacollect 백엔드(Bridge)를 어디로 보낼지 선택. **`local`**(기본) = `g1nation.datacollectBridgeUrl`(로컬 `npm run bridge`). **`nas`** = `g1nation.datacollectBridgeNasUrl`(NAS의 경량 Bridge). `nas`인데 URL이 비어 있으면 안전하게 로컬로 폴백합니다."
        },
        "g1nation.datacollectBridgeUrl": {
          "type": "string",
          "default": "http://127.0.0.1:3002",
          "description": "[local 타깃] Wiki/Datacollect MCP Bridge URL. /benchmark, /youtube, /wikify chat slash commands route here. The Bridge must be running (`npm run bridge` in the Datacollect project)."
        },
        "g1nation.datacollectBridgeNasUrl": {
          "type": "string",
          "default": "",
          "markdownDescription": "[nas 타깃] NAS에서 도는 경량 Bridge URL (예: `https://your-nas-domain` 또는 `http://nas-ip:3002`). `datacollectBridgeTarget`을 `nas`로 두면 여기로 호출합니다. 비워두면 로컬로 폴백."
        },
        "g1nation.datacollectBridgeNasToken": {
          "type": "string",
          "default": "",
          "markdownDescription": "[nas 타깃] NAS Bridge가 요구하는 `x-bridge-token` 값(Bridge의 `BRIDGE_AUTH_TOKEN`과 일치). `nas` 타깃일 때만 요청 헤더에 실립니다. 로컬 타깃에는 영향 없음."
        },
        "g1nation.datacollectSavePath": {
          "type": "string",
          "default": "",
          "markdownDescription": "`/benchmark` 등 Datacollect slash 명령 결과물(markdown)을 저장할 폴더. **비워두면** Bridge 기본 위치(Bridge의 `WIKI_RAW_PATH` 환경변수)에 저장됩니다 — 코드/설정 어디에도 절대경로가 박히지 않습니다. 특정 폴더로 저장하려면 절대경로를 입력하세요. Astra Settings 패널의 'Datacollect' 섹션에서도 편집 가능."
        },
        "g1nation.datacollectCrawlDepth": {
          "type": "number",
          "default": 1,
          "minimum": 0,
          "maximum": 3,
          "markdownDescription": "`/benchmark` 사이트맵 크롤 깊이 기본값. 0=루트만, 1=직속 자식, 2=손자, 3=깊은 크롤. 명령어에서 `depth=N`으로 그때그때 덮어쓸 수 있습니다. (단일 페이지 SPA는 깊이를 올려도 1페이지)"
        },
        "g1nation.datacollectMaxPages": {
          "type": "number",
          "default": 8,
          "minimum": 1,
          "maximum": 20,
          "markdownDescription": "`/benchmark` 스캔 최대 페이지 수 기본값. 명령어에서 `pages=N`으로 덮어쓸 수 있습니다. Bridge에서 20으로 상한이 적용됩니다."
        },
        "g1nation.datacollectSynthesisTemperature": {
          "type": "number",
          "default": 0.1,
          "minimum": 0,
          "maximum": 2,
          "markdownDescription": "`/benchmark` LLM 4-렌즈 합성의 temperature. 낮을수록(0.1) 환각·깨진 문자가 줄고 결과가 결정적이며, 높을수록 표현이 다양해집니다. 기본 0.1 권장."
        },
        "g1nation.chatTemperature": {
          "type": "number",
          "default": 0.3,
          "minimum": 0,
          "maximum": 2,
          "markdownDescription": "채팅 응답 생성의 temperature. 낮을수록(0.2~0.3) 한국어 오타·깨진 토큰·환각이 줄고 결과가 안정적이며, 높을수록 표현이 다양해집니다. 분석·업무용은 0.3 권장."
        },
        "g1nation.meetUsesTasks": {
          "type": "boolean",
          "default": true,
          "markdownDescription": "`/meet` 액션 아이템을 **Google Tasks** 에도 등록할지 여부. 시간 없이 마감일만 있는 \"할 일\" 모델. `meetUsesCalendar` 와 독립적으로 토글 가능 — 둘 다 true 면 양쪽 모두 등록. true 로 두려면 OAuth 재연결로 Tasks 스코프 동의 + Google Cloud Console 에서 Tasks API 활성화 필요(처음 1회)."
        },
        "g1nation.meetUsesCalendar": {
          "type": "boolean",
          "default": false,
          "markdownDescription": "`/meet` 액션 아이템을 **Google Calendar** 일정(all-day)으로도 등록할지 여부. **기본 `false`** — Tasks 단독 등록으로 중복 방지 (Tasks 도 캘린더 사이드바에 같이 보이므로 둘 다 켜면 중복). true 로 켜면 Tasks + Calendar 양쪽 모두 등록."
        },
        "g1nation.teamVoiceGuide": {
          "type": "string",
          "default": "",
          "editPresentation": "multilineText",
          "markdownDescription": "`/draft` 외부 커뮤니케이션 초안 작성 시 모든 생성에 적용되는 **팀 보이스 가이드**. 말투/금기 표현/자주 쓰는 표현/회사 약어 정의 등을 자유 형식으로. 예: '회사명은 항상 \"Astra\" 로 표기. 존댓말 기본. 이모지는 슬랙에서만. 약어 ASAP/FYI 사용 금지...' 비워두면 가이드 없이 일반 초안 생성."
        },
        "g1nation.memoryEnabled": {
          "type": "boolean",
          "default": true,
          "description": "Enable layered memory injection before each model response."
        },
        "g1nation.memoryShortTermMessages": {
          "type": "number",
          "default": 8,
          "minimum": 0,
          "description": "Number of recent conversation messages included as short-term memory."
        },
        "g1nation.memoryMediumTermSessions": {
          "type": "number",
          "default": 5,
          "minimum": 0,
          "description": "Number of recent saved chat sessions included as medium-term memory."
        },
        "g1nation.memoryLongTermFiles": {
          "type": "number",
          "default": 6,
          "minimum": 0,
          "description": "Number of relevant Second Brain markdown files included as long-term memory."
        },
        "g1nation.ollamaUrl": {
          "type": "string",
          "default": "http://127.0.0.1:11434",
          "description": "Base URL for Ollama or LM Studio. Default: http://127.0.0.1:11434"
        },
        "g1nation.defaultModel": {
          "type": "string",
          "default": "gemma4:e2b",
          "description": "Default model name to use for chat requests."
        },
        "g1nation.requestTimeout": {
          "type": "number",
          "default": 300,
          "description": "Request timeout in seconds. Default: 300"
        },
        "g1nation.contextLength": {
          "type": "number",
          "default": 32768,
          "minimum": 2048,
          "description": "Model context window in tokens (prompt + generation combined). Set this to the value your loaded model is actually running with in LM Studio / Ollama. Astra budgets prompt and output against this so it never overflows. Default: 32768"
        },
        "g1nation.maxOutputTokens": {
          "type": "number",
          "default": 4096,
          "minimum": 256,
          "description": "Upper bound on tokens generated per response. The effective limit is reduced automatically when the prompt is large so input + output stays within g1nation.contextLength. Default: 4096"
        },
        "g1nation.contextSafetyMargin": {
          "type": "number",
          "default": 2048,
          "minimum": 0,
          "description": "Tokens kept free as a safety buffer for token-count estimation error. Default: 2048"
        },
        "g1nation.contextOverflowPolicy": {
          "type": "string",
          "enum": [
            "stopAtLimit",
            "truncateMiddle",
            "rollingWindow"
          ],
          "default": "stopAtLimit",
          "description": "Fallback behavior (LM Studio) if the prompt still exceeds the context window after Astra's own budgeting. 'stopAtLimit' fails clearly so you notice; 'truncateMiddle'/'rollingWindow' drop content silently. Default: stopAtLimit"
        },
        "g1nation.autoCompactHistory": {
          "type": "boolean",
          "default": true,
          "description": "Automatically drop the oldest conversation messages from the request when the prompt would exceed the context budget (the on-screen chat history is unaffected). Default: true"
        },
        "g1nation.smallModelContextCap": {
          "type": "number",
          "default": 0,
          "minimum": 0,
          "description": "Optional safety knob, OFF by default (0). Some very small models (≤3B) emit an empty/EOS response when given a prompt near their context window even though it nominally fits. If you observe that with a tiny model, set this to e.g. 8192–16384: for ≤3B models only, Astra then budgets the prompt against this smaller effective window instead of g1nation.contextLength. Never applies to 4B+ models. Leave 0 unless you actually hit the issue — it reduces the output-token budget. Default: 0 (disabled)"
        },
        "g1nation.autoContinueOnOutputLimit": {
          "type": "boolean",
          "default": true,
          "description": "When a reply is cut off because it hit the output-token limit, Astra continues it internally (compressed request — original question + the answer so far, not the whole context again) and shows one merged answer, instead of asking you to say \"이어서 작성해줘\". Default: true"
        },
        "g1nation.maxAutoContinuations": {
          "type": "number",
          "default": 4,
          "minimum": 0,
          "maximum": 10,
          "description": "Maximum number of automatic continuation rounds per reply (prevents runaway loops). Raise it (e.g. 5–6) for long-form answers on slow local models; set 0 to disable auto-continuation. Default: 4"
        },
        "g1nation.finalOnlyRetryOnThoughtLeak": {
          "type": "boolean",
          "default": true,
          "description": "If the model emits only hidden reasoning (<think>, <|channel|>thought, \"Thinking Process:\" …) and no user-visible answer, Astra silently re-asks it for the final answer only. Hidden reasoning is never shown either way. Default: true"
        },
        "g1nation.lmStudio.idleTimeoutMs": {
          "type": "number",
          "default": 300000,
          "minimum": 0,
          "description": "Auto-eject the loaded LM Studio model after this many milliseconds of inactivity. Set to 0 to disable. Default: 300000 (5 minutes)."
        },
        "g1nation.lmStudio.autoLoadOnSelect": {
          "type": "boolean",
          "default": true,
          "description": "Automatically load LM Studio models into memory when selected from the Astra sidebar."
        },
        "g1nation.lmStudio.sampling.topP": {
          "type": "number",
          "default": 0.9,
          "minimum": 0,
          "maximum": 1,
          "description": "Nucleus sampling cutoff. Small / quantized models often spew wrong-neighbour tokens (한글 깨짐: 붕괴→붕점) when the tail is wide. Lower (0.8–0.9) tightens; 1.0 disables. Applied to both SDK and REST paths."
        },
        "g1nation.lmStudio.sampling.topK": {
          "type": "number",
          "default": 20,
          "minimum": 0,
          "description": "Top-K sampling cutoff. 0 disables. Default 20 — tighter for small models, raise to 40–80 for large models that already sample well."
        },
        "g1nation.lmStudio.sampling.minP": {
          "type": "number",
          "default": 0.05,
          "minimum": 0,
          "maximum": 1,
          "description": "Min-P floor — discards tokens with probability below this fraction of the top token. Good defence against rare-token glitches. 0 disables."
        },
        "g1nation.lmStudio.sampling.repeatPenalty": {
          "type": "number",
          "default": 1.1,
          "minimum": 1,
          "maximum": 2,
          "description": "Repeat / frequency penalty to curb stutter (것입니다서입니다…). 1.0 disables. Values 1.05–1.2 are typical."
        },
        "g1nation.lmStudio.statsInBudget": {
          "type": "boolean",
          "default": true,
          "description": "Show token/s and time-to-first-token from LM Studio prediction stats in the context-budget badge after each turn (SDK path only)."
        },
        "g1nation.lmStudio.draftModel": {
          "type": "string",
          "default": "",
          "description": "[Speculative decoding] LM Studio model key of a small draft model (e.g. 'gemma-2b-it') used to accelerate the main model. Empty disables. 1.5–3x throughput on large models. The draft must be downloaded in LM Studio (load is automatic on first use)."
        },
        "g1nation.lmStudio.load.flashAttention": {
          "type": "boolean",
          "default": true,
          "description": "[Load option] Enable Flash Attention when loading models. Faster generation + lower memory on compatible hardware, especially helpful for long contexts. Default: true."
        },
        "g1nation.lmStudio.load.gpuOffloadRatio": {
          "type": "string",
          "default": "max",
          "description": "[Load option] How much of the model to offload to GPU. 'max' = all (default), 'off' = CPU only, or a number 0–1 (e.g. '0.5' = half). Numeric strings are parsed."
        },
        "g1nation.lmStudio.load.offloadKVCacheToGpu": {
          "type": "boolean",
          "default": true,
          "description": "[Load option] Keep KV cache on GPU memory. Faster but requires VRAM headroom. Default: true."
        },
        "g1nation.lmStudio.load.keepModelInMemory": {
          "type": "boolean",
          "default": true,
          "description": "[Load option] Prevent the model from being swapped out of system memory. Improves interactive responsiveness; raises RAM use. Default: true."
        },
        "g1nation.lmStudio.load.useFp16ForKVCache": {
          "type": "boolean",
          "default": false,
          "description": "[Load option] Store KV cache in FP16 (halves cache memory). Tiny quality impact for most models — try if you run out of VRAM at long contexts. Default: false."
        },
        "g1nation.lmStudio.load.evalBatchSize": {
          "type": "number",
          "default": 0,
          "minimum": 0,
          "description": "[Load option] Token batch size during evaluation. 0 = engine default. Higher (512–1024) improves prefill speed on GPU at the cost of memory."
        },
        "g1nation.localBrainPath": {
          "type": "string",
          "default": "",
          "description": "Folder path for your local Second Brain knowledge base. Leave empty to use the default folder."
        },
        "g1nation.brainProfiles": {
          "type": "array",
          "default": [],
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "Stable brain profile id."
              },
              "name": {
                "type": "string",
                "description": "Display name shown in the Astra brain selector."
              },
              "localBrainPath": {
                "type": "string",
                "description": "Local folder path used as this brain's markdown knowledge base."
              },
              "secondBrainRepo": {
                "type": "string",
                "description": "Optional Git repository URL for this brain."
              },
              "description": {
                "type": "string",
                "description": "Short note shown under the active brain status."
              }
            }
          },
          "description": "Multiple brain profiles. Each item supports id, name, localBrainPath, secondBrainRepo, and description."
        },
        "g1nation.activeBrainId": {
          "type": "string",
          "default": "",
          "description": "Active brain profile id used for the current chat context."
        },
        "g1nation.secondBrainRepo": {
          "type": "string",
          "default": "",
          "description": "Optional GitHub repository URL used for Second Brain sync."
        },
        "g1nation.autoPushBrain": {
          "type": "boolean",
          "default": false,
          "description": "Automatically commit and push Second Brain changes after updates."
        },
        "g1nation.maxContextSize": {
          "type": "number",
          "default": 32000,
          "description": "Maximum character count for active file context. Default: 32000"
        },
        "g1nation.maxAutoSteps": {
          "type": "number",
          "default": 50,
          "description": "Maximum autonomous steps the agent can take per request. Default: 50"
        },
        "g1nation.dryRun": {
          "type": "boolean",
          "default": false,
          "description": "If enabled, the agent will ask for approval before committing any file changes."
        },
        "g1nation.telegram.enabled": {
          "type": "boolean",
          "default": false,
          "description": "Enable the Telegram bot integration. When on, Astra polls a bot you configure and replies to incoming messages. Off by default — Astra remains 100% local until you opt in."
        },
        "g1nation.telegram.allowedChatIds": {
          "type": "array",
          "default": [],
          "items": {
            "type": "number"
          },
          "description": "Optional allowlist of Telegram chat IDs that may message the bot. When empty, every chat that messages the bot is accepted (use with caution)."
        },
        "g1nation.telegram.defaultAgent": {
          "type": "string",
          "default": "",
          "description": "Agent name (matches an entry in the Agent ↔ Knowledge map) used to scope Second Brain retrieval for Telegram replies. Empty falls back to the map's defaultAgent, then to whole-brain search."
        },
        "g1nation.telegram.agentByChatId": {
          "type": "object",
          "default": {},
          "additionalProperties": {
            "type": "string"
          },
          "description": "Per-chat override of the Telegram agent. Keys are stringified chat IDs, values are agent names from the knowledge map. Overrides telegram.defaultAgent for the listed chats."
        },
        "g1nation.telegram.contextChunks": {
          "type": "number",
          "default": 6,
          "minimum": 0,
          "maximum": 20,
          "description": "How many Second Brain excerpts to inject into Telegram replies. Set 0 to disable RAG (plain prompt only)."
        },
        "g1nation.skillKnowledgeMapPath": {
          "type": "string",
          "default": "",
          "description": "Absolute path to the agent ↔ knowledge mapping JSON. When empty, defaults to '<workspace>/.astra/agent-knowledge-map.json'."
        },
        "g1nation.skillKnowledgeMap": {
          "type": "object",
          "default": {},
          "description": "Inline fallback for the agent ↔ knowledge mapping. Used only when the JSON file is missing. Shape: { defaultAgent?, agents: [{ name, knowledgeFolders, model?, description? }] }. Folder paths can be absolute, ~-prefixed, or relative to the active brain root."
        },
        "g1nation.agentSkillsPath": {
          "type": "string",
          "default": "",
          "description": "Absolute path to the agent skills folder (`.agent/skills/*.md`). When empty, defaults to '<workspace>/.agent/skills'. Use this on Windows or when your skills live outside the workspace."
        },
        "g1nation.embeddingModel": {
          "type": "string",
          "default": "",
          "description": "Embedding model registered in LM Studio / Ollama (e.g. 'text-embedding-bge-small-en-v1.5', 'nomic-embed-text', 'multilingual-e5-small'). When empty, Astra uses TF-IDF only. When set, the brain is embedded lazily in the background and retrieval blends TF-IDF + cosine similarity for synonym / paraphrase matching. Multilingual models are recommended for Korean content."
        },
        "g1nation.embeddingBlendAlpha": {
          "type": "number",
          "default": 0.5,
          "minimum": 0,
          "maximum": 1,
          "description": "Hybrid score blend: 0 = pure TF-IDF (sparse / keyword), 1 = pure embedding cosine (dense / semantic), 0.5 = balanced. Only used when g1nation.embeddingModel is set. Default 0.5."
        },
        "g1nation.chunkLevelRetrieval": {
          "type": "boolean",
          "default": false,
          "description": "섹션 청크 단위 검색 (Phase 1-가). 켜면 두뇌 검색이 파일이 아니라 '##' 헤딩 기준 섹션 청크 단위로 색인·스코어링하고 매치된 섹션을 주입합니다. 긴 다주제 문서의 검색 정밀도를 높입니다. '검색 평가 실행'으로 끄고/켜고 비교해 보세요. 기본 false."
        },
        "g1nation.chunkTargetChars": {
          "type": "number",
          "default": 1200,
          "minimum": 400,
          "maximum": 4000,
          "description": "섹션 청크의 목표 길이(문자). 섹션이 이보다 길면 문단 경계로 더 쪼갭니다. chunkLevelRetrieval 가 켜져 있을 때만 사용. 값을 바꾸면 다음 검색 때 청크 색인이 재생성됩니다. 기본 1200."
        },
        "g1nation.conflictHighlightingEnabled": {
          "type": "boolean",
          "default": true,
          "description": "Conflict Surface — 검색된 출처에서 충돌/논란 신호 감지 시 [CONFLICT WARNINGS] 블록을 시스템 프롬프트에 주입. LLM 이 상충되는 관점을 명시하고 사용자 판단에 위임하도록. 기본 켜짐."
        },
        "g1nation.conflictSeverityThreshold": {
          "type": "string",
          "enum": [
            "low",
            "medium",
            "high"
          ],
          "default": "medium",
          "description": "Conflict 자기-신호 surface 시 최소 severity 임계. low=가장 민감(노이즈 가능), medium=균형(기본), high=강한 충돌만."
        },
        "g1nation.conflictCrossDocEnabled": {
          "type": "boolean",
          "default": true,
          "description": "교차-문서 발산 감지 — 같은 주제 ≥2 chunks 의 본문 Jaccard < 0.30 인 잠재 모순 쌍을 [CONFLICT WARNINGS] 에 함께 표시. 기본 켜짐."
        },
        "g1nation.coveEnabled": {
          "type": "boolean",
          "default": true,
          "description": "Chain-of-Verification (CoVe) — 답변 *작성 전* 그라운딩 체크리스트를 시스템 프롬프트에 주입해 모델이 self-verify 하도록. 할루시네이션 방지 + 출처 명확화. 기본 켜짐."
        },
        "g1nation.coveTopSourcesCount": {
          "type": "number",
          "default": 5,
          "minimum": 1,
          "maximum": 15,
          "description": "CoVe 체크리스트에 나열할 상위 출처 개수. 너무 많으면 프롬프트 비대, 너무 적으면 그라운딩 부족. 기본 5."
        },
        "g1nation.coveStrictMode": {
          "type": "boolean",
          "default": false,
          "description": "CoVe Strict 모드 — 모든 사실 주장 뒤에 출처 ID [S1] 형식으로 inline 인용 강제. 답변이 학술적·verbose 해질 수 있어 기본 off."
        },
        "g1nation.actionabilityEnabled": {
          "type": "boolean",
          "default": true,
          "description": "Actionability Scoring — '현재 작업 상태' 신호(최근 슬래시 명령 + 열린 파일) 로 검색 결과를 재가중. 지금 작업 중인 컨텍스트와 직접 연결된 문서를 우선. 기본 켜짐."
        },
        "g1nation.distillationEnabled": {
          "type": "boolean",
          "default": true,
          "description": "Distillation Loop — stale Episodic Memory 를 LongTerm 'episode-digest' 로 승급해 검색 노이즈 방지. /memory distill 수동 + 세션 종료 시 자동 트리거. 기본 켜짐."
        },
        "g1nation.distillationAgeThresholdDays": {
          "type": "number",
          "default": 30,
          "minimum": 1,
          "maximum": 365,
          "description": "며칠 이상 지난 episode 를 distill 대상으로 할지. 기본 30일."
        },
        "g1nation.distillationIntervalDays": {
          "type": "number",
          "default": 7,
          "minimum": 1,
          "maximum": 90,
          "description": "자동 distillation 의 최소 간격 (일). 마지막 실행 후 이 일수가 지나야 재실행. 기본 7일."
        },
        "g1nation.distillationArchiveMode": {
          "type": "string",
          "enum": [
            "mark-promoted",
            "archive-file"
          ],
          "default": "mark-promoted",
          "description": "Distillation 후 원본 episode 처리: 'mark-promoted'=플래그만 (파일 보존, 기본), 'archive-file'=memory/episodes/archive/ 로 파일 이동."
        },
        "g1nation.hierarchicalReweightEnabled": {
          "type": "boolean",
          "default": true,
          "description": "Hierarchical Context Window — 질의·문서 추상도(concrete/operational/strategic) 매칭으로 검색 결과 재가중. 같은 레벨 +15%, 양 끝 mismatch -30%. LLM 호출 없음, 결정적. 기본 켜짐."
        },
        "g1nation.semanticRerankEnabled": {
          "type": "boolean",
          "default": false,
          "description": "Semantic Re-ranking — 검색된 selectedChunks 의 순서를 LLM 한 번 호출로 의도-부합도 순 재정렬. 매 turn 1회 추가 LLM 호출 (latency 비용). 기본 OFF — 명시적 on 해야 함."
        },
        "g1nation.semanticRerankModel": {
          "type": "string",
          "default": "",
          "description": "Semantic Re-ranking 전용 모델 ID. 비면 defaultModel 사용. 빠른 작은 모델(예: gemma2:2b) 권장 — latency 줄임."
        },
        "g1nation.semanticRerankCandidateK": {
          "type": "number",
          "default": 15,
          "minimum": 2,
          "maximum": 30,
          "description": "Re-rank 대상 상위 후보 개수. 많을수록 quality↑ latency↑ token↑. 기본 15."
        },
        "g1nation.semanticRerankTimeoutSec": {
          "type": "number",
          "default": 8,
          "minimum": 1,
          "maximum": 60,
          "description": "Re-rank LLM 호출 타임아웃 (초). 초과 시 원순서 그대로 — 검색 실패 안 됨. 기본 8."
        },
        "g1nation.intentClarificationEnabled": {
          "type": "boolean",
          "default": true,
          "description": "Intent Clarification — 모호 질의(환경/대상/범위/포맷/마감 누락) 감지 시 LLM 에게 추측 답변보다 *역질문 우선* 지시. 기본 켜짐."
        },
        "g1nation.intentClarificationStrictness": {
          "type": "string",
          "enum": [
            "low",
            "medium",
            "high"
          ],
          "default": "medium",
          "description": "모호 판정 임계. low=가장 덜 묻기(2개+ missing), medium=균형(1개+), high=가장 자주 묻기 (1개+ OR 짧은 질의+trigger)."
        },
        "g1nation.citationTraceEnabled": {
          "type": "boolean",
          "default": true,
          "description": "Citation Trace — 답변 끝에 사용된 출처를 *출처:* 한 줄로 정리 지시. CoVe Strict 의 가벼운 형제, 항상 ON 권장. 기본 켜짐."
        },
        "g1nation.selfCheckEnabled": {
          "type": "boolean",
          "default": false,
          "description": "Post-hoc Self-Check — 답변 완료 후 별도 LLM 호출 1회로 검증 (답변 직접도/그라운딩/논리 모순). 결과를 답변 아래 footer 한 줄로 표시. 매 turn 추가 LLM 호출 (latency 비용). 기본 OFF — 명시적 opt-in."
        },
        "g1nation.selfCheckModel": {
          "type": "string",
          "default": "",
          "description": "Self-check 전용 모델 ID. 비면 defaultModel 사용. 빠른 작은 모델 (예: gemma2:2b) 권장."
        },
        "g1nation.selfCheckTimeoutSec": {
          "type": "number",
          "default": 6,
          "minimum": 1,
          "maximum": 60,
          "description": "Self-check LLM 호출 타임아웃 (초). 초과 시 흐릿한 한 줄 footer 로 fallback. 기본 6."
        },
        "g1nation.glossaryEnabled": {
          "type": "boolean",
          "default": true,
          "description": "Terminology Dictionary — 사용자 편집 글로서리(.astra/glossary.md) 를 시스템 프롬프트에 주입. 표준 표기 강제 + 답변 직전 Term Check. 파일 없으면 자동 no-op. 기본 켜짐."
        },
        "g1nation.glossaryPath": {
          "type": "string",
          "default": ".astra/glossary.md",
          "description": "Glossary 파일 상대 경로 (workspace root 기준). 기본 '.astra/glossary.md'."
        },
        "g1nation.glossaryMaxBodyLength": {
          "type": "number",
          "default": 4000,
          "minimum": 500,
          "maximum": 20000,
          "description": "Glossary 본문 시스템 프롬프트 cap (chars). 너무 크면 토큰 비용↑. 초과분은 잘림. 기본 4000."
        },
        "g1nation.termValidatorEnabled": {
          "type": "boolean",
          "default": true,
          "description": "Post-gen Term Validator — 답변 완료 후 글로서리 forbidden 단어 결정론적 정규식 스캔. Terminology Dictionary 의 instructional 지시를 deterministic 검증으로 보완. LLM 호출 없음, 매 turn 안전. footer 한 줄 표시. 기본 켜짐."
        },
        "g1nation.knowledgeMix.secondBrainWeight": {
          "type": "number",
          "default": 50,
          "minimum": 0,
          "maximum": 100,
          "description": "Knowledge Mix (0–100): how heavily the assistant should lean on Second Brain evidence vs. its own general knowledge. 0 = Second Brain disabled (model knowledge only). 50 = balanced (legacy default). 100 = Second Brain is the primary evidence; model knowledge only fills harmless background. Per-agent overrides in the Agent Mapping panel win over this global value."
        },
        "g1nation.workflow.multiAgentMode": {
          "type": "string",
          "enum": [
            "auto",
            "always",
            "off"
          ],
          "default": "auto",
          "markdownDescription": "Multi-Agent(5단계) 파이프라인 발동 모드.\n\n- `auto` (기본): 작은 모델(≤4B) 감지, 큰 prompt(컨텍스트의 30%+), 명시적 키워드(보고서/리뷰/심층 분석…), 또는 사용자가 `multiAgentEnabled`를 켰을 때 자동으로 발동. 짧은 인사·잡담은 제외.\n- `always`: 인사·잡담을 제외한 모든 요청에 5단계 파이프라인 사용. 작은 모델로도 답변이 한 번에 끝나지 않는다면 이 모드가 안정적.\n- `off`: 기존 키워드/길이 휴리스틱 + 수동 `multiAgentEnabled` 토글만 사용 (legacy 동작)."
        },
        "g1nation.workflow.autoCtxFractionThreshold": {
          "type": "number",
          "default": 0.3,
          "minimum": 0.05,
          "maximum": 0.95,
          "markdownDescription": "`workflow.multiAgentMode = auto` 일 때, prompt 토큰이 효과적 context window 의 이 비율(0~1)을 넘으면 5단계 파이프라인을 강제 발동. 기본 0.30 — 작은 모델이 input으로 컨텍스트의 30% 이상을 먹기 시작하면 한 번에 답하려다 EOS/잘림이 잘 발생한다."
        },
        "g1nation.chunkedSwitchTokens": {
          "type": "number",
          "default": 50000,
          "minimum": 1000,
          "markdownDescription": "**입력 prompt 가 이 토큰 수 *미만* 이면 Multi-Agent(chunked) 파이프라인 발동 안 함** — 모델이 단일 호출로 처리.\n\n키워드(\"요약\", \"리뷰\", \"보고서\" 등) 나 길이(>240자) 같은 트리거가 있어도 입력이 이 임계값 미만이면 무시되고 한 번에 답변 → 답변 속도 크게 향상.\n\n기본 **50000** — 대부분의 사용 환경에 적합. 매우 작은 컨텍스트 모델로 큰 입력을 자주 다룬다면 OOM 방지 차원에서 이 값을 낮출 수 있음."
        },
        "g1nation.chunkedMaxSections": {
          "type": "number",
          "default": 3,
          "minimum": 1,
          "maximum": 10,
          "markdownDescription": "**Chunked 파이프라인이 답변을 쪼갤 수 있는 최대 섹션 수.**\n\n실제 LLM 호출 횟수 = `1 (outline) + N (sections) + 1 (polish)` = **2 + N 회**.\n- `1` → 총 3회 (가장 빠름, 답변이 단순할 때만 적합)\n- `3` (기본) → 총 5회\n- `5` → 총 7회 (세분화 필요할 때만)\n\n작을수록 답변 속도 빠름, 클수록 답변이 더 세분화돼서 복잡한 보고서·기획서에 유리. 기본 3 — 일반 채팅에 적합."
        },
        "g1nation.polishPersonaOverride": {
          "type": "string",
          "default": "",
          "editPresentation": "multilineText",
          "markdownDescription": "**ChunkedWriter 의 polish 단계 system prompt 를 직접 정의** — 답변 톤·구조를 도메인에 맞게 커스텀.\n\n비워두면 기본 polish persona (한 줄 요약 + subheading + 5-check self-review) 사용. 텍스트를 입력하면 그 내용이 그대로 polish 단계의 system prompt 로 들어갑니다.\n\n사용 예: 격식체 답변, 반말 답변, 법률·마케팅·의료 등 도메인 특화 톤. 사용자가 Astra Settings 패널 textarea 로 편집 권장."
        },
        "g1nation.liveStreamTokens": {
          "type": "boolean",
          "default": true,
          "markdownDescription": "모델 토큰을 받는 즉시 채팅 버블에 흘려보낼지 여부.\n\n- `true` (기본): 토큰을 받는 즉시 표시 → TTFT 체감 속도 향상. 생성이 끝나면 sanitize 된 최종 답변으로 `streamReplace` 가 한 번에 교체하므로 control token 노출은 잠깐만 가능.\n- `false`: 토큰을 내부에서만 누적, sanitize(`<|channel|>thought` / `<think>` / `Thinking Process:` 등 제거) 끝난 **최종 답변만 한 번에** 표시. 모델의 control token 이 잠깐이라도 화면에 노출되는 누설을 원천 차단."
        },
        "g1nation.outputFormat": {
          "type": "string",
          "enum": [
            "plain",
            "markdown"
          ],
          "default": "plain",
          "markdownDescription": "최종 답변 표시 방식.\n\n- `plain` (기본): 모델이 무심코 내보낸 마크다운 마커(`##`, `**`, `__`, `> `, `* ` 등)를 후처리로 모두 제거. 섹션 라벨 텍스트(예: `핵심 요약`)는 유지되지만 헤더 마커는 사라져 깔끔한 plain text 로 보임. 작은 로컬 모델이 학습된 습관으로 `## 다음 한 수` 같은 마커를 흘리는 문제 차단.\n- `markdown`: legacy 동작. 모델 출력을 그대로 렌더러에 넘김."
        },
        "g1nation.chronicleAutoRecord": {
          "type": "boolean",
          "default": true,
          "markdownDescription": "자동 기록 (Project Chronicle Auto-Record).\n\n- `true` (기본): 매 chat turn 후 의미 있는 대화(planning / decision / bug / development / discussion 유형 자동 판별)를 활성 프로젝트의 Chronicle 폴더에 자동 저장.\n- `false`: 자동 저장 OFF. 수동 기록 (도구 ▾ 의 기록 항목, `/wiki` 등) 은 계속 가능.\n\n사이드바 **도구 ▾** 메뉴의 `자동 기록` 토글로 즉시 전환 가능 — 설정 패널까지 들어갈 필요 없음."
        },
        "g1nation.company.intentClassifierModel": {
          "type": "string",
          "default": "",
          "description": "Model used to classify whether an incoming chat message in 1인 기업 모드 is a (a) casual chat / question, (b) follow-up on the previous round, or (c) a brand-new task that should trigger the full work pipeline. Empty → uses g1nation.defaultModel. Pick a fast small model (e.g. gemma 4 e2b) so the classifier doesn't add latency before every chat send. The classifier runs once per user message and returns a one-token-ish JSON verdict, so even slow hardware sees minimal overhead."
        },
        "g1nation.company.disableIntentClassifier": {
          "type": "boolean",
          "default": false,
          "description": "Bypass the intent classifier and always run the full work pipeline on every chat message in 1인 기업 모드 (legacy behaviour). Enable this only if you want every input — including 'thanks', 'show me X again' — to dispatch all agents. Off by default because most chat messages aren't new work and shouldn't burn a full pipeline."
        },
        "g1nation.company.autoSelectPipeline": {
          "type": "boolean",
          "default": true,
          "description": "Let the intent classifier *automatically switch* to the pipeline it recommends for this turn (e.g. short '기획서까지만' for a planning ask, full '풀 프로덕트' for an end-to-end product). Your explicitly-activated pipeline is bypassed for the round but the activation itself isn't changed. On by default — the classifier's read of the user's intent (especially explicit signals like '기획만'·'디자인만') should be honoured. Set to false if you'd rather always run the pipeline you activated yourself."
        },
        "g1nation.company.intentAlignmentMode": {
          "type": "string",
          "enum": [
            "off",
            "smart",
            "strict"
          ],
          "default": "smart",
          "description": "Intent Alignment — turn user prompts into an explicit Requirement Contract (C-G-C-F-Q) before dispatching a pipeline. 'off' = legacy, pipeline runs immediately. 'smart' (default) = run when confidence is high, else show a confirmation card; ask up to N rounds of clarifying questions if information is missing. 'strict' = always show the contract card and require user confirmation, regardless of confidence. Goal: stop agents from silently guessing at the user's mental model."
        },
        "g1nation.company.intentAlignmentMaxRounds": {
          "type": "number",
          "minimum": 1,
          "maximum": 5,
          "default": 3,
          "description": "Maximum back-and-forth rounds the Intent Alignment analyzer is allowed to ask before forcing a 'confirm or cancel' card (it stops asking new questions and shows the current contract for user approval). Each round = one LLM call. Default 3."
        },
        "g1nation.selfReflector.enabled": {
          "type": "boolean",
          "default": false,
          "description": "Self-Reflector Phase A — append a [Self-Reflector Check] block at the end of every substantive LLM answer (Consistency / Completeness / Accuracy, plus References / Paths for code answers). Zero extra LLM calls — the rule lives in the system prompt and the model self-imposes the checklist. OFF by default: the check block is an internal verification log that leaks into the user-facing answer and reads as unpolished. Enable only if you want that transparency block visible."
        },
        "g1nation.hollowCheck.enabled": {
          "type": "boolean",
          "default": true,
          "description": "Hollow Code Check — `<create_file>` 등 action-tag 로 만든 파일이 *빈 깡통* (empty class, stub-only function, imports-only) 인지 정규식 스캔. LLM 호출 0 — 휴리스틱만. v2.2.203 부터 Phase A 와 분리, 기본 ON. 검출 시 actionReport 에 ⚠️ 라인 추가 + (autoRetry ON 이면) 자동 재작업."
        },
        "g1nation.hollowCheck.autoRetry": {
          "type": "boolean",
          "default": true,
          "description": "Hollow 감지 시 1회 자동 재작업 — Phase B (externalVerification) 와 분리. dev-impl 같은 코드 작성 stage 가 빈 깡통을 뱉으면 같은 모델·context 에 빈 깡통 지적을 prepend 해서 1회 재호출. 재작업 결과도 hollow 재검사. 기본 ON. OFF 시 hollow 검출 후 경고만 표시."
        },
        "g1nation.selfReflector.externalVerification": {
          "type": "boolean",
          "default": false,
          "description": "Self-Reflector Phase B — after every 1인 기업 specialist response, run a *separate* LLM call to verify the output from an outside-context perspective (catches the 'same model self-validates' blind spot). Failed checks trigger one auto-revise round. Off by default — adds +1 LLM call per dispatched stage."
        },
        "g1nation.selfReflector.executionVerification": {
          "type": "boolean",
          "default": false,
          "description": "Self-Reflector Phase C — after a code file is created via <create_file>, automatically run the language's syntax check (Python: py_compile, JS: node --check, TS: project tsc --noEmit). Failures are surfaced in the action report so the user (and the agent on a follow-up) can see exactly what broke. Requires the language toolchain installed on the user's machine. Off by default."
        },
        "g1nation.company.pixelOffice.enabled": {
          "type": "boolean",
          "default": true,
          "description": "Show the Pixel Office visualisation panel above the chat — a small pixel-office-style display that mirrors the agent's current pipeline status (analyzing, need_clarification, executing, reviewing, waiting_approval, done, etc.) and the current task / contract / open questions. UI layer only; turning it off does not change any agent behaviour."
        },
        "g1nation.company.pixelOffice.bubbles": {
          "type": "boolean",
          "default": true,
          "description": "Show short comic-style speech bubbles above the Pixel Office character on status changes / key events (e.g. '코드 들어간다', '잠깐, 이건 다시 보자', '좋아, 끝났다!'). Bubbles are purely narrative — they never influence the agent's decisions. Disable for a quieter UI."
        },
        "g1nation.google.clientId": {
          "type": "string",
          "default": "",
          "scope": "machine",
          "markdownDescription": "Google OAuth Client ID — `console.cloud.google.com/apis/credentials` → OAuth 2.0 Client ID (Desktop app) 생성 후 복사. **`Astra: Google Calendar OAuth 연결 (쓰기) 🔐`** 명령을 한 번 실행하면 이 값이 자동으로 채워집니다. Calendar + Sheets 둘 다 이 자격증명을 공유.\n\n_scope: machine — Settings Sync 로 다른 기기에 공유되지 않음._"
        },
        "g1nation.google.clientSecret": {
          "type": "string",
          "default": "",
          "scope": "machine",
          "markdownDescription": "Google OAuth Client Secret — Client ID 와 같은 페이지에서 발급. Desktop app OAuth 의 secret 은 Google 가이드상 *진짜 비밀이 아닌 식별자* 지만, settings.json 에 그대로 들어가므로 git 커밋 / 화면 공유 시 주의.\n\n_scope: machine — Settings Sync 안 됨._"
        },
        "g1nation.google.calendarId": {
          "type": "string",
          "default": "primary",
          "markdownDescription": "일정을 등록할 Google Calendar 식별자. 기본 `primary` (본인 메인 캘린더). 특정 캘린더 쓰려면 Calendar 설정 → 캘린더 통합 → 'Calendar ID' 복사 (예: `xxxxxxx@group.calendar.google.com`)."
        },
        "g1nation.google.defaultEventDurationMinutes": {
          "type": "number",
          "default": 60,
          "minimum": 5,
          "maximum": 1440,
          "description": "end / duration 둘 다 없는 일정의 기본 길이 (분). agent 가 회의록에서 시각만 추출하고 종료 시각은 명시 안 했을 때 적용."
        },
        "g1nation.google.icalUrl": {
          "type": "string",
          "default": "",
          "scope": "machine",
          "markdownDescription": "Google Calendar **비공개 iCal URL** — 읽기 전용 모드용. `calendar.google.com/calendar/u/0/r/settings` → 본인 캘린더 → '캘린더 통합' → '비공개 주소(iCal 형식)' 복사. **이 URL 을 가진 사람은 본인 캘린더 모든 일정을 볼 수 있으니 절대 공개 금지.**\n\n_scope: machine — Settings Sync 안 됨. OAuth 와는 별개 — 둘 다 셋업해도 되고 한 쪽만 해도 됨._"
        },
        "g1nation.google.icalDaysAhead": {
          "type": "number",
          "default": 14,
          "minimum": 1,
          "maximum": 90,
          "description": "iCal 캐시에 포함할 다가오는 일정 기간 (일). default 14 = 2주치."
        },
        "g1nation.providers.openrouter.enabled": {
          "type": "boolean",
          "default": false,
          "description": "OpenRouter cloud provider 활성화 — Claude/Gemini/GPT 등 100+ 모델을 OpenAI 호환 API 로 사용. API key 는 Astra Settings 패널에서 등록 (Secret Storage 사용, settings.json 비저장)."
        },
        "g1nation.providers.openrouter.defaultModel": {
          "type": "string",
          "default": "",
          "description": "OpenRouter 의 기본 모델 (예: 'anthropic/claude-3.5-sonnet'). 모델 선택 시 'openrouter:<model>' 형식으로 사이드바 dropdown 에 표시."
        },
        "g1nation.providers.anthropic.enabled": {
          "type": "boolean",
          "default": false,
          "description": "Anthropic Claude 직접 API 활성화. OpenRouter 보다 마진 적고 prompt caching 등 native 기능 사용 가능. API key 는 Secret Storage."
        },
        "g1nation.providers.anthropic.defaultModel": {
          "type": "string",
          "default": "claude-3-5-sonnet-20241022",
          "description": "Anthropic 의 기본 모델. 예: 'claude-3-5-sonnet-20241022', 'claude-3-5-haiku-20241022'."
        },
        "g1nation.providers.gemini.enabled": {
          "type": "boolean",
          "default": false,
          "description": "Google Gemini 직접 API 활성화. 1M context (gemini-1.5-pro), 무료 tier 등 native 기능 사용. API key 는 Secret Storage."
        },
        "g1nation.providers.gemini.defaultModel": {
          "type": "string",
          "default": "gemini-2.0-flash-exp",
          "description": "Gemini 의 기본 모델. 예: 'gemini-2.0-flash-exp', 'gemini-1.5-pro', 'gemini-1.5-flash'."
        },
        "g1nation.devilAgent.enabled": {
          "type": "boolean",
          "default": false,
          "markdownDescription": "**Devil's Advocate (도현) 활성화** — 매 답변 직후 별도 LLM 호출로 *비판적 sparring partner* 가 한 문단 반박. 사용자의 사고를 *수동적 수용 → 능동적 방어* 로 전환시키는 게 목표. Local LLM 동일 모델 재사용, ~10-15% 추가 비용."
        },
        "g1nation.stocks.watcherEnabled": {
          "type": "boolean",
          "default": true,
          "markdownDescription": "**주식 자동 모니터링 활성화** — VS Code 시작 시 watcher 가동. KST 09:00 / 15:00 자동 트리거: Yahoo 현재가 갱신 → Google Sheets 동기화 → 텔레그램 보고서. false 면 슬래시 명령으로만 수동 실행."
        },
        "g1nation.stocks.spreadsheetId": {
          "type": "string",
          "default": "",
          "markdownDescription": "**Stocks Google Sheets ID** — `https://docs.google.com/spreadsheets/d/<여기>/...` URL 의 ID 부분. 미설정이면 watcher / `/stocks sync` 가 silent skip. OAuth scope 은 calendar 와 공유 (sheets scope 이미 포함)."
        },
        "g1nation.stocks.sheetSwing": {
          "type": "string",
          "default": "Sheet1",
          "markdownDescription": "**스윙/중기 종목 시트 탭 이름.** 기본 'Sheet1'. spreadsheet 안에 이 이름의 탭이 미리 존재해야 함."
        },
        "g1nation.stocks.sheetLong": {
          "type": "string",
          "default": "Sheet2",
          "markdownDescription": "**장기투자 종목 시트 탭 이름.** 기본 'Sheet2'."
        },
        "g1nation.stocks.sheetUltraLow": {
          "type": "string",
          "default": "Sheet3",
          "markdownDescription": "**저평가우량주 시트 탭 이름.** 기본 'Sheet3'."
        },
        "g1nation.stocks.telegramChatId": {
          "type": "number",
          "default": 0,
          "markdownDescription": "**Stocks 보고서 전용 텔레그램 chatId** — fallback. `g1nation.telegram.allowedChatIds` 가 비어있을 때만 사용. 0 = 미설정."
        }
      }
    }
  },
  "scripts": {
    "vscode:prepublish": "npm run test && npm run compile",
    "compile": "esbuild src/extension.ts --bundle --platform=node --external:vscode --outfile=out/extension.js",
    "watch": "tsc -watch -p ./",
    "test": "jest --no-cache --forceExit",
    "test:engine": "jest tests/agentEngine.test.ts --verbose --no-cache",
    "pretest": "npm run compile"
  },
  "devDependencies": {
    "@types/jest": "^29.5.14",
    "@types/node": "18.x",
    "@types/vscode": "^1.80.0",
    "@vercel/ncc": "^0.38.4",
    "esbuild": "^0.28.0",
    "jest": "^29.7.0",
    "ts-jest": "^29.4.9",
    "typescript": "^5.1.3"
  },
  "dependencies": {
    "@lmstudio/sdk": "^1.5.0",
    "pdf-parse": "^2.4.5"
  }
}
