1 line
1.6 MiB
Plaintext
1 line
1.6 MiB
Plaintext
{"repo": "agentscope-ai/qwenpaw", "files": {"src/qwenpaw/providers/provider_manager.py": {"e524dfe78d4a69b2441d8f611935bbe0b28cb7da": {"t": 1785674141.2052708, "n": 83823, "h": "ed2bd12a6def38a9", "c": "# -*- coding: utf-8 -*-\n\"\"\"A Manager class to handle all providers, including built-in and custom ones.\nIt provides a unified interface to manage providers, such as listing available\nproviders, adding/removing custom providers, and fetching provider details.\"\"\"\n\nimport asyncio\nimport json\nimport logging\nimport os\nfrom typing import Dict, List\n\nfrom agentscope.model import ChatModelBase\nfrom pydantic import BaseModel\n\nfrom qwenpaw.exceptions import ModelNotFoundException\n\nfrom ..config.config import ModelSlotConfig\nfrom ..constant import SECRET_DIR\nfrom ..exceptions import ProviderError\nfrom ..security.secret_store import (\n PROVIDER_SECRET_FIELDS,\n decrypt_dict_fields,\n encrypt_dict_fields,\n is_encrypted,\n)\nfrom .anthropic_provider import AnthropicProvider\nfrom .context_windows import DEFAULT_CONTEXT_WINDOW\nfrom .dashscope_provider import DashScopeProvider\nfrom .gemini_provider import GeminiProvider\nfrom .lmstudio_provider import LMStudioProvider\nfrom .ollama_provider import OllamaProvider\nfrom .openai_provider import (\n GitHubModelsProvider,\n KiloProvider,\n OpenAIProvider,\n OpenCodeProvider,\n)\nfrom .openai_response_provider import OpenAIResponseProvider\nfrom .openrouter_provider import OpenRouterProvider\nfrom .provider import ModelInfo, Provider, ProviderInfo\n\nlogger = logging.getLogger(__name__)\n\n# -------------------------------------------------------\n# Built-in provider definitions and their default models.\n# -------------------------------------------------------\n\nMODELSCOPE_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"Qwen/Qwen3.5-122B-A10B\",\n name=\"Qwen3.5-122B-A10B\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"ZhipuAI/GLM-5\",\n name=\"GLM-5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nDASHSCOPE_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"qwen3.7-max\",\n name=\"Qwen3.7 Max\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n thinking_enabled=True,\n relay_reasoning=False,\n ),\n ModelInfo(\n id=\"qwen3.7-plus\",\n name=\"Qwen3.7 Plus\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n thinking_enabled=True,\n relay_reasoning=False,\n ),\n ModelInfo(\n id=\"qwen3.6-plus\",\n name=\"Qwen3.6 Plus\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n thinking_enabled=True,\n relay_reasoning=False,\n ),\n ModelInfo(\n id=\"deepseek-v4-pro\",\n name=\"DeepSeek-V4 Pro\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n thinking_enabled=True,\n thinking_param_style=\"effort\",\n reasoning_effort_options=[\"high\", \"max\"],\n relay_reasoning=False,\n ),\n ModelInfo(\n id=\"glm-5.2\",\n name=\"GLM-5.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n thinking_enabled=True,\n thinking_param_style=\"effort\",\n reasoning_effort_options=[\"high\", \"max\"],\n relay_reasoning=False,\n ),\n]\n\nMIMO_TOKENPLAN_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"mimo-v2.5-pro\",\n name=\"MiMo V2.5 Pro\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"mimo-v2.5\",\n name=\"MiMo V2.5\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n]\n\nALIYUN_TOKENPLAN_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"qwen3.7-plus\",\n name=\"Qwen3.7 Plus\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3.7-max\",\n name=\"Qwen3.7 Max\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3.6-plus\",\n name=\"Qwen3.6 Plus\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3.6-flash\",\n name=\"Qwen3.6 Flash\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-v4-pro\",\n name=\"DeepSeek-V4 Pro\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-v4-flash\",\n name=\"DeepSeek-V4 Flash\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-v3.2\",\n name=\"DeepSeek-V3.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5.2\",\n name=\"GLM-5.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5.1\",\n name=\"GLM-5.1\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5\",\n name=\"GLM-5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.5\",\n name=\"MiniMax M2.5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2.6\",\n name=\"Kimi K2.6\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2.5\",\n name=\"Kimi K2.5\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n]\n\nALIYUN_CODINGPLAN_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"qwen3.6-plus\",\n name=\"Qwen3.6 Plus\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3.5-plus\",\n name=\"Qwen3.5 Plus\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5.2\",\n name=\"GLM-5.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5.1\",\n name=\"GLM-5.1\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5\",\n name=\"GLM-5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-4.7\",\n name=\"GLM-4.7\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.5\",\n name=\"MiniMax M2.5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2.5\",\n name=\"Kimi K2.5\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3-max-2026-01-23\",\n name=\"Qwen3 Max 2026-01-23\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3-coder-next\",\n name=\"Qwen3 Coder Next\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3-coder-plus\",\n name=\"Qwen3 Coder Plus\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nZHIPU_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"glm-4.7-flash\",\n name=\"GLM-4.7-Flash\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"glm-5\",\n name=\"glm-5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5.2\",\n name=\"glm-5.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5.1\",\n name=\"glm-5.1\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5-turbo\",\n name=\"glm-5-turbo\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5v-turbo\",\n name=\"glm-5v-turbo\",\n supports_image=True,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nOPENAI_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"gpt-5.2\",\n name=\"GPT-5.2\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-5\",\n name=\"GPT-5\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-5-mini\",\n name=\"GPT-5 Mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-5-nano\",\n name=\"GPT-5 Nano\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4.1\",\n name=\"GPT-4.1\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4.1-mini\",\n name=\"GPT-4.1 Mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4.1-nano\",\n name=\"GPT-4.1 Nano\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"o3\",\n name=\"o3\",\n supports_image=True,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"o4-mini\",\n name=\"o4-mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4o\",\n name=\"GPT-4o\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4o-mini\",\n name=\"GPT-4o Mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n]\n\nKILO_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"kilo-auto/free\",\n name=\"Kilo Auto (Free Router)\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"nvidia/nemotron-3-ultra-550b-a55b:free\",\n name=\"Nemotron 3 Ultra 550B\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"nvidia/nemotron-3-super-120b-a12b:free\",\n name=\"Nemotron 3 Super 120B\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"poolside/laguna-m.1:free\",\n name=\"Poolside Laguna M.1\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"poolside/laguna-xs.2:free\",\n name=\"Poolside Laguna XS.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"stepfun/step-3.7-flash:free\",\n name=\"Step 3.7 Flash\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"nex-agi/nex-n2-pro:free\",\n name=\"Nex N2 Pro\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n]\n\nOPENCODE_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"deepseek-v4-flash-free\",\n name=\"DeepSeek V4 Flash\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"mimo-v2.5-free\",\n name=\"Mimo V2.5\",\n supports_image=True,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"nemotron-3-ultra-free\",\n name=\"Nemotron 3 Ultra\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"nemotron-3-super-free\",\n name=\"Nemotron 3 Super\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n]\n\nAZURE_OPENAI_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"gpt-5-chat\",\n name=\"GPT-5 Chat\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-5-mini\",\n name=\"GPT-5 Mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-5-nano\",\n name=\"GPT-5 Nano\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4.1\",\n name=\"GPT-4.1\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4.1-mini\",\n name=\"GPT-4.1 Mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4.1-nano\",\n name=\"GPT-4.1 Nano\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4o\",\n name=\"GPT-4o\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4o-mini\",\n name=\"GPT-4o Mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n]\n\nMINIMAX_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"MiniMax-M3\",\n name=\"MiniMax M3\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.7\",\n name=\"MiniMax M2.7\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.7-highspeed\",\n name=\"MiniMax M2.7 Highspeed\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.5\",\n name=\"MiniMax M2.5 (legacy)\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.5-highspeed\",\n name=\"MiniMax M2.5 Highspeed (legacy)\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.1\",\n name=\"MiniMax M2.1 (legacy)\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.1-highspeed\",\n name=\"MiniMax M2.1 Highspeed (legacy)\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2\",\n name=\"MiniMax M2 (legacy)\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nKIMI_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"kimi-k2.5\",\n name=\"Kimi K2.5\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2-0905-preview\",\n name=\"Kimi K2 0905 Preview\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2-0711-preview\",\n name=\"Kimi K2 0711 Preview\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2-turbo-preview\",\n name=\"Kimi K2 Turbo Preview\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2-thinking\",\n name=\"Kimi K2 Thinking\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2-thinking-turbo\",\n name=\"Kimi K2 Thinking Turbo\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nDEEPSEEK_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"deepseek-chat\",\n name=\"DeepSeek Chat\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-reasoner\",\n name=\"DeepSeek Reasoner\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-v4-flash\",\n name=\"DeepSeek V4 Flash\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-v4-pro\",\n name=\"DeepSeek V4 Pro\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nVOLCENGINE_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"doubao-seed-2-0-code-preview-260215\",\n name=\"Doubao-Seed-2.0-Code\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-2-0-pro-260215\",\n name=\"Doubao-Seed-2.0-pro\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-2-0-lite-260428\",\n name=\"Doubao-Seed-2.0-lite\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-code-preview-251028\",\n name=\"Doubao-Seed-Code\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"glm-4-7-251222\",\n name=\"GLM-4.7\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-v3-2-251201\",\n name=\"DeepSeek-V3.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"doubao-seed-1-8-251228\",\n name=\"Doubao-Seed-1.8\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-2-0-mini-260428\",\n name=\"Doubao-Seed-2.0-mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-character-251128\",\n name=\"Doubao-Seed-Character\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nVOLCENGINE_CODINGPLAN_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"doubao-seed-2-0-code-preview-260215\",\n name=\"Doubao-Seed-2.0-Code\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-2-0-pro-260215\",\n name=\"Doubao-Seed-2.0-pro\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-2-0-lite-260428\",\n name=\"Doubao-Seed-2.0-lite\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-code-preview-251028\",\n name=\"Doubao-Seed-Code\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"glm-5.1\",\n name=\"GLM-5.1\",\n supports_image=False,\n supports_video=False,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"minimax-m2.7\",\n name=\"MiniMax-M2.7\",\n supports_image=False,\n supports_video=False,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"kimi-k2.6\",\n name=\"Kimi-K2.6\",\n supports_image=False,\n supports_video=False,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"kimi-k2.5\",\n name=\"Kimi-K2.5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"glm-4-7-251222\",\n name=\"GLM-4.7\",\n supports_image=False,\n supports_video=False,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"deepseek-v3-2-251201\",\n name=\"DeepSeek-V3.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"probed\",\n ),\n]\n\nANTHROPIC_MODELS: List[ModelInfo] = []\n\nGEMINI_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"gemini-3.1-pro-preview\",\n name=\"Gemini 3.1 Pro Preview\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gemini-3-flash-preview\",\n name=\"Gemini 3 Flash Preview\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gemini-3.1-flash-lite-preview\",\n name=\"Gemini 3.1 Flash Lite Preview\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gemini-2.5-pro\",\n name=\"Gemini 2.5 Pro\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gemini-2.5-flash\",\n name=\"Gemini 2.5 Flash\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gemini-2.5-flash-lite\",\n name=\"Gemini 2.5 Flash Lite\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gemini-2.0-flash\",\n name=\"Gemini 2.0 Flash\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n]\n\nPROVIDER_MODELSCOPE = OpenAIProvider(\n id=\"modelscope\",\n name=\"ModelScope\",\n base_url=\"https://api-inference.modelscope.cn/v1\",\n api_key_prefix=\"ms\",\n models=MODELSCOPE_MODELS,\n freeze_url=True,\n)\n\nPROVIDER_DASHSCOPE = DashScopeProvider(\n id=\"dashscope\",\n name=\"DashScope\",\n base_url=\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n api_key_prefix=\"sk\",\n models=DASHSCOPE_MODELS,\n provider_group=\"aliyun\",\n provider_group_name=\"Aliyun\",\n provider_variant=\"dashscope\",\n meta={\n \"base_url_options\": [\n {\n \"label\": \"China (Beijing)\",\n \"value\": \"https://dashscope.aliyuncs.com/\"\n \"compatible-mode/v1\",\n },\n {\n \"label\": \"International (Singapore)\",\n \"value\": \"https://dashscope-intl.aliyuncs.com/\"\n \"compatible-mode/v1\",\n },\n {\n \"label\": \"US (Virginia)\",\n \"value\": \"https://dashscope-us.aliyuncs.com/\"\n \"compatible-mode/v1\",\n },\n ],\n },\n)\n\nPROVIDER_ALIYUN_CODINGPLAN = OpenAIProvider(\n id=\"aliyun-codingplan\",\n name=\"Aliyun Coding Plan (China)\",\n base_url=\"https://coding.dashscope.aliyuncs.com/v1\",\n api_key_prefix=\"sk-sp\",\n models=ALIYUN_CODINGPLAN_MODELS,\n support_connection_check=False,\n freeze_url=True,\n provider_group=\"aliyun\",\n provider_group_name=\"Aliyun\",\n provider_variant=\"coding_plan_cn\",\n)\n\nPROVIDER_ALIYUN_CODINGPLAN_INTL = OpenAIProvider(\n id=\"aliyun-codingplan-intl\",\n name=\"Aliyun Coding Plan (International)\",\n base_url=\"https://coding-intl.dashscope.aliyuncs.com/v1\",\n api_key_prefix=\"sk-sp\",\n models=ALIYUN_CODINGPLAN_MODELS,\n support_connection_check=False,\n freeze_url=True,\n provider_group=\"aliyun\",\n provider_group_name=\"Aliyun\",\n provider_variant=\"coding_plan_intl\",\n)\n\nPROVIDER_ALIYUN_TOKENPLAN = OpenAIProvider(\n id=\"aliyun-tokenplan\",\n name=\"Aliyun Token Plan\",\n base_url=(\n \"https://token-plan.cn-beijing.maas.aliyuncs.com/\" \"compatible-mode/v1\"\n ),\n api_key_prefix=\"sk-sp\",\n models=ALIYUN_TOKENPLAN_MODELS,\n support_connection_check=False,\n freeze_url=True,\n provider_group=\"aliyun\",\n provider_group_name=\"Aliyun\",\n provider_variant=\"token_plan\",\n)\n\nPROVIDER_ALIYUN_TOKENPLAN_INTL = OpenAIProvider(\n id=\"aliyun-tokenplan-intl\",\n name=\"Aliyun Token Plan (International)\",\n base_url=(\n \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/\"\n \"compatible-mode/v1\"\n ),\n api_key_prefix=\"sk-sp\",\n models=ALIYUN_TOKENPLAN_MODELS,\n support_connection_check=False,\n freeze_url=True,\n provider_group=\"aliyun\",\n provider_group_name=\"Aliyun\",\n provider_variant=\"token_plan_intl\",\n)\n\nPROVIDER_ZHIPU_CN = OpenAIProvider(\n id=\"zhipu-cn\",\n name=\"Zhipu (BigModel)\",\n base_url=\"https://open.bigmodel.cn/api/paas/v4\",\n api_key_prefix=\"\",\n models=ZHIPU_MODELS,\n freeze_url=True,\n provider_group=\"zhipu\",\n provider_group_name=\"Zhipu\",\n provider_variant=\"open_platform_cn\",\n meta={\"is_free_tier\": True},\n)\n\nPROVIDER_ZHIPU_CN_CODINGPLAN = OpenAIProvider(\n id=\"zhipu-cn-codingplan\",\n name=\"Zhipu Coding Plan (BigModel)\",\n base_url=\"https://open.bigmodel.cn/api/coding/paas/v4\",\n api_key_prefix=\"\",\n models=ZHIPU_MODELS,\n freeze_url=True,\n support_connection_check=False,\n provider_group=\"zhipu\",\n provider_group_name=\"Zhipu\",\n provider_variant=\"coding_plan_cn\",\n)\n\nPROVIDER_ZHIPU_INTL = OpenAIProvider(\n id=\"zhipu-intl\",\n name=\"Zhipu (Z.AI)\",\n base_url=\"https://api.z.ai/api/paas/v4\",\n api_key_prefix=\"\",\n models=ZHIPU_MODELS,\n freeze_url=True,\n provider_group=\"zhipu\",\n provider_group_name=\"Zhipu\",\n provider_variant=\"open_platform_intl\",\n)\n\nPROVIDER_ZHIPU_INTL_CODINGPLAN = OpenAIProvider(\n id=\"zhipu-intl-codingplan\",\n name=\"Zhipu Coding Plan (Z.AI)\",\n base_url=\"https://api.z.ai/api/coding/paas/v4\",\n api_key_prefix=\"\",\n models=ZHIPU_MODELS,\n freeze_url=True,\n support_connection_check=False,\n provider_group=\"zhipu\",\n provider_group_name=\"Zhipu\",\n provider_variant=\"coding_plan_intl\",\n)\n\nPROVIDER_QWENPAW = OpenAIProvider(\n id=\"qwenpaw-local\",\n name=\"QwenPaw Local\",\n is_local=True,\n require_api_key=False,\n)\n\nPROVIDER_OPENAI = OpenAIProvider(\n id=\"openai\",\n name=\"OpenAI\",\n base_url=\"https://api.openai.com/v1\",\n api_key_prefix=\"sk-\",\n models=OPENAI_MODELS,\n freeze_url=True,\n)\n\nPROVIDER_OPENAI_RESPONSE = OpenAIResponseProvider(\n id=\"openai-response\",\n name=\"OpenAI (Response API)\",\n base_url=\"https://api.openai.com/v1\",\n api_key_prefix=\"sk-\",\n chat_model=\"OpenAIResponseModel\",\n models=OPENAI_MODELS,\n freeze_url=True,\n)\n\nPROVIDER_OPENCODE = OpenCodeProvider(\n id=\"opencode\",\n name=\"OpenCode\",\n base_url=\"https://opencode.ai/zen/v1\",\n api_key_prefix=\"\",\n models=OPENCODE_MODELS,\n require_api_key=False,\n meta={\n \"base_url_options\": [\n {\"label\": \"OpenCode\", \"value\": \"https://opencode.ai/zen/v1\"},\n {\"label\": \"OpenCode Go\", \"value\": \"https://opencode.ai/zen/go/v1\"},\n ],\n \"is_free_tier\": True,\n },\n freeze_url=False,\n)\n\nPROVIDER_KILO = KiloProvider(\n id=\"kilo\",\n name=\"Kilo Code\",\n base_url=\"https://api.kilo.ai/api/gateway\",\n api_key_prefix=\"\",\n models=KILO_MODELS,\n require_api_key=False,\n meta={\"is_free_tier\": True},\n freeze_url=True,\n)\n\nPROVIDER_AZURE_OPENAI = OpenAIProvider(\n id=\"azure-openai\",\n name=\"Azure OpenAI\",\n api_key_prefix=\"\",\n models=AZURE_OPENAI_MODELS,\n)\n\nPROVIDER_MINIMAX = AnthropicProvider(\n id=\"minimax\",\n name=\"MiniMax (International)\",\n base_url=\"https://api.minimax.io/anthropic\",\n models=MINIMAX_MODELS,\n chat_model=\"AnthropicChatModel\",\n freeze_url=True,\n support_connection_check=False,\n provider_group=\"minimax\",\n provider_group_name=\"MiniMax\",\n provider_variant=\"open_platform_intl\",\n)\n\nPROVIDER_MINIMAX_CN = AnthropicProvider(\n id=\"minimax-cn\",\n name=\"MiniMax (China)\",\n base_url=\"https://api.minimaxi.com/anthropic\",\n models=MINIMAX_MODELS,\n chat_model=\"AnthropicChatModel\",\n freeze_url=True,\n support_connection_check=False,\n provider_group=\"minimax\",\n provider_group_name=\"MiniMax\",\n provider_variant=\"open_platform_cn\",\n)\n\nPROVIDER_KIMI_CN = OpenAIProvider(\n id=\"kimi-cn\",\n name=\"Kimi (China)\",\n base_url=\"https://api.moonshot.cn/v1\",\n api_key_prefix=\"\",\n models=KIMI_MODELS,\n freeze_url=True,\n provider_group=\"kimi\",\n provider_group_name=\"Kimi\",\n provider_variant=\"open_platform_cn\",\n)\n\nPROVIDER_KIMI_INTL = OpenAIProvider(\n id=\"kimi-intl\",\n name=\"Kimi (International)\",\n base_url=\"https://api.moonshot.ai/v1\",\n api_key_prefix=\"\",\n models=KIMI_MODELS,\n freeze_url=True,\n provider_group=\"kimi\",\n provider_group_name=\"Kimi\",\n provider_variant=\"open_platform_intl\",\n)\n\nKIMI_CODINGPLAN_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"kimi-for-coding\",\n name=\"Kimi for Coding\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nPROVIDER_KIMI_CODINGPLAN = OpenAIProvider(\n id=\"kimi-codingplan\",\n name=\"Kimi Coding Plan\",\n base_url=\"https://api.kimi.com/coding/v1\",\n api_key_prefix=\"sk-kimi-\",\n models=KIMI_CODINGPLAN_MODELS,\n freeze_url=True,\n support_connection_check=False,\n provider_group=\"kimi\",\n provider_group_name=\"Kimi\",\n provider_variant=\"coding_plan\",\n)\n\nPROVIDER_DEEPSEEK = OpenAIProvider(\n id=\"deepseek\",\n name=\"DeepSeek\",\n base_url=\"https://api.deepseek.com\",\n api_key_prefix=\"sk-\",\n models=DEEPSEEK_MODELS,\n freeze_url=True,\n)\n\nPROVIDER_ANTHROPIC = AnthropicProvider(\n id=\"anthropic\",\n name=\"Anthropic\",\n base_url=\"https://api.anthropic.com\",\n api_key_prefix=\"sk-ant-\",\n models=ANTHROPIC_MODELS,\n chat_model=\"AnthropicChatModel\",\n freeze_url=False,\n)\n\nPROVIDER_GEMINI = GeminiProvider(\n id=\"gemini\",\n name=\"Google Gemini\",\n base_url=\"https://generativelanguage.googleapis.com\",\n api_key_prefix=\"\",\n models=GEMINI_MODELS,\n chat_model=\"GeminiChatModel\",\n freeze_url=True,\n meta={\n \"is_free_tier\": True,\n },\n)\n\nPROVIDER_OLLAMA = OllamaProvider(\n id=\"ollama\",\n name=\"Ollama\",\n is_local=True,\n require_api_key=False,\n support_model_discovery=True,\n generate_kwargs={\"max_tokens\": None},\n)\n\nPROVIDER_OPENROUTER = OpenRouterProvider(\n id=\"openrouter\",\n name=\"OpenRouter\",\n base_url=\"https://openrouter.ai/api/v1\",\n api_key_prefix=\"sk-or-v1-\",\n models=[],\n freeze_url=True,\n meta={\n \"supports_oauth\": True,\n \"is_free_tier\": True,\n },\n)\n\nGITHUB_MODELS_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"openai/gpt-4o-mini\",\n name=\"GPT-4o Mini\",\n supports_image=True,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"openai/gpt-4o\",\n name=\"GPT-4o\",\n supports_image=True,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n]\n\nPROVIDER_GITHUB_MODELS = GitHubModelsProvider(\n id=\"github-models\",\n name=\"GitHub Models\",\n base_url=\"https://models.github.ai/inference\",\n api_key_prefix=\"ghp_\",\n api_key_prefixes=[\"ghp_\", \"github_pat_\"],\n models=GITHUB_MODELS_MODELS,\n freeze_url=False,\n meta={\n \"is_free_tier\": True,\n },\n)\n\n\nPROVIDER_LMSTUDIO = LMStudioProvider(\n id=\"lmstudio\",\n name=\"LM Studio\",\n is_local=True,\n base_url=\"http://localhost:1234/v1\",\n require_api_key=False,\n api_key_prefix=\"\",\n support_model_discovery=True,\n generate_kwargs={\"max_tokens\": None},\n)\n\nPROVIDER_SILICONFLOW_CN = OpenAIProvider(\n id=\"siliconflow-cn\",\n name=\"SiliconFlow (China)\",\n base_url=\"https://api.siliconflow.cn/v1\",\n api_key_prefix=\"sk-\",\n models=[],\n freeze_url=True,\n require_api_key=True,\n provider_group=\"siliconflow\",\n provider_group_name=\"SiliconFlow\",\n provider_variant=\"china\",\n meta={\n \"is_free_tier\": True,\n },\n)\n\nPROVIDER_SILICONFLOW_INTL = OpenAIProvider(\n id=\"siliconflow-intl\",\n name=\"SiliconFlow (International)\",\n base_url=\"https://api.siliconflow.com/v1\",\n api_key_prefix=\"sk-\",\n models=[],\n freeze_url=True,\n require_api_key=True,\n provider_group=\"siliconflow\",\n provider_group_name=\"SiliconFlow\",\n provider_variant=\"international\",\n meta={\n \"is_free_tier\": True,\n },\n)\n\nPROVIDER_VOLCENGINE_CN = OpenAIProvider(\n id=\"volcengine-cn\",\n name=\"Volcano Engine\",\n base_url=\"https://ark.cn-beijing.volces.com/api/v3\",\n api_key_prefix=\"\",\n models=VOLCENGINE_MODELS,\n freeze_url=True,\n support_model_discovery=False,\n provider_group=\"volcengine\",\n provider_group_name=\"Volcano Engine\",\n provider_variant=\"open_platform\",\n)\n\nPROVIDER_VOLCENGINE_CN_CODINGPLAN = OpenAIProvider(\n id=\"volcengine-cn-codingplan\",\n name=\"Volcano Engine Coding Plan\",\n base_url=\"https://ark.cn-beijing.volces.com/api/coding/v3\",\n api_key_prefix=\"\",\n models=VOLCENGINE_CODINGPLAN_MODELS,\n support_connection_check=False,\n freeze_url=True,\n support_model_discovery=False,\n provider_group=\"volcengine\",\n provider_group_name=\"Volcano Engine\",\n provider_variant=\"coding_plan\",\n)\n\nPROVIDER_MIMO_TOKENPLAN = OpenAIProvider(\n id=\"mimo-tokenplan\",\n name=\"Xiaomi MiMo Token Plan\",\n base_url=\"https://token-plan-cn.xiaomimimo.com/v1\",\n api_key_prefix=\"\",\n models=MIMO_TOKENPLAN_MODELS,\n freeze_url=True,\n)\n\n\nclass ProviderManager: # pylint: disable=too-many-public-methods\n \"\"\"A manager class to handle all providers,\n including built-in and custom ones.\"\"\"\n\n _instance = None\n\n def __init__(self) -> None:\n # Initialize provider manager, load providers from registry and store\n # any necessary state (e.g., cached models).\n self.builtin_providers: Dict[str, Provider] = {}\n self.custom_providers: Dict[str, Provider] = {}\n self.plugin_providers: Dict[str, Dict] = {} # Plugin providers\n self.active_model: ModelSlotConfig | None = None\n self.root_path = SECRET_DIR / \"providers\"\n self.builtin_path = self.root_path / \"builtin\"\n self.custom_path = self.root_path / \"custom\"\n self.plugin_path = self.root_path / \"plugin\" # Plugin provider configs\n self._prepare_disk_storage()\n self._init_builtins()\n try:\n self._migrate_legacy_providers()\n except Exception as e:\n logger.warning(\"Failed to migrate legacy providers: %s\", e)\n self._init_from_storage()\n self._apply_default_annotations()\n\n def _prepare_disk_storage(self):\n \"\"\"Prepare directory structure\"\"\"\n for path in [\n self.root_path,\n self.builtin_path,\n self.custom_path,\n self.plugin_path,\n ]:\n path.mkdir(parents=True, exist_ok=True)\n try:\n os.chmod(path, 0o700) # Restrict permissions for security\n except Exception:\n pass\n\n def _init_builtins(self):\n self._add_builtin(PROVIDER_QWENPAW)\n self._add_builtin(PROVIDER_OLLAMA)\n self._add_builtin(PROVIDER_LMSTUDIO)\n self._add_builtin(PROVIDER_OPENROUTER)\n self._add_builtin(PROVIDER_GITHUB_MODELS)\n self._add_builtin(PROVIDER_MODELSCOPE)\n self._add_builtin(PROVIDER_DASHSCOPE)\n self._add_builtin(PROVIDER_ALIYUN_CODINGPLAN)\n self._add_builtin(PROVIDER_ALIYUN_CODINGPLAN_INTL)\n self._add_builtin(PROVIDER_ALIYUN_TOKENPLAN)\n self._add_builtin(PROVIDER_ALIYUN_TOKENPLAN_INTL)\n self._add_builtin(PROVIDER_OPENCODE)\n self._add_builtin(PROVIDER_KILO)\n self._add_builtin(PROVIDER_OPENAI)\n self._add_builtin(PROVIDER_OPENAI_RESPONSE)\n self._add_builtin(PROVIDER_AZURE_OPENAI)\n self._add_builtin(PROVIDER_ANTHROPIC)\n self._add_builtin(PROVIDER_GEMINI)\n self._add_builtin(PROVIDER_DEEPSEEK)\n self._add_builtin(PROVIDER_KIMI_CN)\n self._add_builtin(PROVIDER_KIMI_INTL)\n self._add_builtin(PROVIDER_KIMI_CODINGPLAN)\n self._add_builtin(PROVIDER_MINIMAX_CN)\n self._add_builtin(PROVIDER_MINIMAX)\n self._add_builtin(PROVIDER_ZHIPU_CN)\n self._add_builtin(PROVIDER_ZHIPU_CN_CODINGPLAN)\n self._add_builtin(PROVIDER_ZHIPU_INTL)\n self._add_builtin(PROVIDER_ZHIPU_INTL_CODINGPLAN)\n self._add_builtin(PROVIDER_SILICONFLOW_CN)\n self._add_builtin(PROVIDER_SILICONFLOW_INTL)\n self._add_builtin(PROVIDER_VOLCENGINE_CN)\n self._add_builtin(PROVIDER_VOLCENGINE_CN_CODINGPLAN)\n self._add_builtin(PROVIDER_MIMO_TOKENPLAN)\n\n def _add_builtin(self, provider: Provider):\n self.builtin_providers[provider.id] = provider\n\n async def list_provider_info(self) -> List[ProviderInfo]:\n tasks = [\n provider.get_info() for provider in self.builtin_providers.values()\n ]\n tasks += [\n provider.get_info() for provider in self.custom_providers.values()\n ]\n # Add plugin providers - directly return their ProviderInfo\n for plugin_provider in self.plugin_providers.values():\n provider_info = plugin_provider[\"info\"]\n # Plugin providers store ProviderInfo directly, no need to\n # instantiate\n tasks.append(self._get_plugin_provider_info(provider_info))\n\n provider_infos = await asyncio.gather(*tasks)\n return list(provider_infos)\n\n async def _get_plugin_provider_info(\n self,\n provider_info: ProviderInfo,\n ) -> ProviderInfo:\n \"\"\"Helper to return plugin provider info as async task.\"\"\"\n return provider_info\n\n @staticmethod\n def _normalize_provider_id(provider_id: str) -> str:\n \"\"\"Normalize provider ID for backward compatibility.\n\n Maps legacy 'copaw-local' to 'qwenpaw-local'.\n \"\"\"\n if provider_id == \"copaw-local\":\n return \"qwenpaw-local\"\n return provider_id\n\n def get_provider(self, provider_id: str) -> Provider | None:\n # Return a provider instance by its ID. This will be used to create\n # chat model instances for the agent.\n # Normalize provider ID for backward compatibility\n provider_id = self._normalize_provider_id(provider_id)\n # Check plugin providers first\n if provider_id in self.plugin_providers:\n plugin_provider = self.plugin_providers[provider_id]\n provider_info = plugin_provider[\"info\"]\n provider_class = plugin_provider[\"class\"]\n # Instantiate with **dict unpacking for Pydantic BaseModel\n return provider_class(**provider_info.model_dump())\n if provider_id in self.builtin_providers:\n return self.builtin_providers[provider_id]\n if provider_id in self.custom_providers:\n return self.custom_providers[provider_id]\n return None\n\n async def get_provider_info(self, provider_id: str) -> ProviderInfo | None:\n provider = self.get_provider(provider_id)\n return await provider.get_info() if provider else None\n\n def get_active_model(self) -> ModelSlotConfig | None:\n # Return the currently active provider/model configuration.\n return self.active_model\n\n def update_provider(self, provider_id: str, config: Dict) -> bool:\n # Update the configuration of a provider (e.g., base URL, API key).\n # This will be called when the user edits a provider's settings in the\n # UI. It should update the in-memory provider instance and persist the\n # changes to providers.json.\n # Normalize provider ID for backward compatibility\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n return False\n provider.update_config(config)\n\n # Determine save location\n is_builtin = provider_id in self.builtin_providers\n is_plugin = provider_id in self.plugin_providers\n\n if is_plugin:\n # Update plugin provider info in memory (convert Provider to\n # ProviderInfo)\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n # Save to plugin path (separate from builtin)\n self._save_plugin_provider(provider)\n else:\n self._save_provider(provider, is_builtin=is_builtin)\n\n return True\n\n def start_local_model_resume(self, local_manager) -> None:\n \"\"\"Schedule background restore of the active local model server.\"\"\"\n task = asyncio.create_task(\n self._resume_local_model(local_manager),\n name=\"qwenpaw-local-model-resume\",\n )\n task.add_done_callback(self._on_local_model_resume_done)\n\n @staticmethod\n def _on_local_model_resume_done(task: asyncio.Task[None]) -> None:\n \"\"\"Log unexpected failures from background local model restore.\"\"\"\n if task.cancelled():\n return\n\n exc = task.exception()\n if exc is not None:\n logger.warning(\n \"Background local model restore failed: %s\",\n exc,\n exc_info=exc,\n )\n logger.info(\"Background local model restore completed\")\n\n async def fetch_provider_models(\n self,\n provider_id: str,\n save: bool = True,\n ) -> List[ModelInfo]:\n \"\"\"Fetch the list of available models from a provider.\n\n Args:\n provider_id: The ID of the provider to fetch models from.\n save: If True, save the discovered models to the provider\n configuration. Defaults to True.\n\n Returns:\n List of ModelInfo objects representing available models.\n \"\"\"\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n return []\n try:\n models = await provider.fetch_models()\n if save:\n provider.extra_models = models\n # Save provider config to appropriate location\n is_plugin = provider_id in self.plugin_providers\n if is_plugin:\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n self._save_plugin_provider(provider)\n else:\n self._save_provider(\n provider,\n is_builtin=provider_id in self.builtin_providers,\n )\n return models\n except Exception as e:\n logger.warning(\n \"Failed to fetch models for provider '%s': %s\",\n provider_id,\n e,\n )\n return []\n\n def _resolve_custom_provider_id(self, provider_id: str) -> str:\n \"\"\"Resolve provider ID conflicts for a custom provider.\"\"\"\n base_id = provider_id\n if base_id in self.builtin_providers:\n base_id = f\"{base_id}-custom\"\n\n resolved_id = base_id\n while (\n resolved_id in self.builtin_providers\n or resolved_id in self.custom_providers\n ):\n resolved_id = f\"{resolved_id}-new\"\n\n return resolved_id\n\n async def add_custom_provider(self, provider_data: ProviderInfo):\n # Add a new custom provider with the given data. This will update the\n # providers.json file and make the new provider available in the UI.\n provider_payload = provider_data.model_dump()\n # ``max_input_length`` equal to the historical 128K default is only\n # distinguishable from an omitted value while the request model still\n # carries Pydantic's field-presence information. Preserve that intent\n # before model_dump/storage erase it. This is deliberately scoped to\n # the user-facing custom-provider ingestion path: legacy provider JSON\n # serialized every default field, so applying the same inference while\n # loading from disk would incorrectly mark all old 128K defaults as\n # explicit overrides.\n for field in (\"models\", \"extra_models\"):\n source_models = getattr(provider_data, field, ())\n payload_models = provider_payload.get(field, ())\n for source, payload in zip(source_models, payload_models):\n if \"max_input_length\" in source.model_fields_set:\n payload[\"max_input_length_configured\"] = True\n provider_payload[\"id\"] = self._resolve_custom_provider_id(\n provider_data.id,\n )\n provider_payload[\"is_custom\"] = True\n provider = self._provider_from_data(\n provider_payload,\n ) # Validate provider data\n # For custom providers, we assume they don't support connection check\n # without model config, to avoid false negatives in the UI.\n provider.support_connection_check = False\n self.custom_providers[provider.id] = provider\n self._save_provider(provider, is_builtin=False)\n return await provider.get_info()\n\n def remove_custom_provider(self, provider_id: str) -> bool:\n # Remove a custom provider by its ID. This will update the\n # providers.json file and remove the provider from the UI.\n if provider_id in self.custom_providers:\n del self.custom_providers[provider_id]\n provider_path = self.custom_path / f\"{provider_id}.json\"\n if provider_path.exists():\n os.remove(provider_path)\n return True\n return False\n\n async def activate_model(self, provider_id: str, model_id: str):\n # Set the active provider and model for the agent. This will update\n # providers.json and determine which provider/model is used when the\n # agent creates chat model instances.\n # Normalize provider ID for backward compatibility\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n raise ProviderError(\n message=f\"Provider '{provider_id}' not found.\",\n )\n if not provider.has_model(model_id):\n raise ModelNotFoundException(\n model_name=f\"{provider_id}/{model_id}\",\n details={\"provider_id\": provider_id, \"model_id\": model_id},\n )\n self.active_model = ModelSlotConfig(\n provider_id=provider_id,\n model=model_id,\n )\n self.save_active_model(self.active_model)\n\n self.maybe_probe_multimodal(provider_id, model_id)\n\n def maybe_probe_multimodal(self, provider_id: str, model_id: str) -> None:\n \"\"\"Schedule multimodal probing for a model if capability is unknown.\"\"\"\n provider = self.get_provider(provider_id)\n # Auto-probe multimodal if not yet probed\n for model in provider.models + provider.extra_models:\n if model.id == model_id and model.supports_multimodal is None:\n asyncio.create_task(\n self._auto_probe_multimodal(provider_id, model_id),\n )\n break\n\n async def _auto_probe_multimodal(\n self,\n provider_id: str,\n model_id: str,\n ) -> None:\n \"\"\"Background probe that doesn't block model activation.\"\"\"\n try:\n result = await self.probe_model_multimodal(provider_id, model_id)\n logger.info(\n \"Auto-probe for %s/%s: image=%s, video=%s\",\n provider_id,\n model_id,\n result.get(\"supports_image\"),\n result.get(\"supports_video\"),\n )\n # Heal a poisoned ``rejects_media`` cache entry: if the\n # probe actually saw the image, force ``rejects_media`` to\n # False so subsequent ``_reasoning`` calls stop stripping\n # media. Without this, a stale entry written from an\n # unrelated 400 (request too large, malformed block fields)\n # would silently drop every future image.\n if result.get(\"supports_image\"):\n from .model_capability_cache import get_capability_cache\n\n get_capability_cache().learn(\n f\"{provider_id}:{model_id}\",\n \"rejects_media\",\n False,\n )\n except Exception as e:\n logger.warning(\"Auto-probe multimodal failed: %s\", e)\n\n async def add_model_to_provider(\n self,\n provider_id: str,\n model_info: ModelInfo,\n ) -> ProviderInfo:\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n raise ProviderError(\n message=f\"Provider '{provider_id}' not found.\",\n )\n added, error_message = await provider.add_model(model_info)\n if not added:\n raise ProviderError(\n message=error_message,\n details={\n \"provider_id\": provider_id,\n \"model_id\": model_info.id,\n },\n )\n\n # Save provider config to appropriate location\n is_plugin = provider_id in self.plugin_providers\n if is_plugin:\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n self._save_plugin_provider(provider)\n else:\n self._save_provider(\n provider,\n is_builtin=provider_id in self.builtin_providers,\n )\n return await provider.get_info()\n\n async def update_model_config(\n self,\n provider_id: str,\n model_id: str,\n config: Dict,\n ) -> ProviderInfo:\n \"\"\"Update per-model configuration and persist to disk.\"\"\"\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n raise ProviderError(\n message=f\"Provider '{provider_id}' not found.\",\n )\n if not provider.update_model_config(model_id, config):\n raise ModelNotFoundException(\n model_name=f\"{provider_id}/{model_id}\",\n details={\"provider_id\": provider_id, \"model_id\": model_id},\n )\n\n # Save provider config to appropriate location\n is_plugin = provider_id in self.plugin_providers\n if is_plugin:\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n self._save_plugin_provider(provider)\n else:\n self._save_provider(\n provider,\n is_builtin=provider_id in self.builtin_providers,\n )\n return await provider.get_info()\n\n async def delete_model_from_provider(\n self,\n provider_id: str,\n model_id: str,\n ) -> ProviderInfo:\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n raise ProviderError(\n message=f\"Provider '{provider_id}' not found.\",\n )\n await provider.delete_model(model_id=model_id)\n\n # Save provider config to appropriate location\n is_plugin = provider_id in self.plugin_providers\n if is_plugin:\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n self._save_plugin_provider(provider)\n else:\n self._save_provider(\n provider,\n is_builtin=provider_id in self.builtin_providers,\n )\n return await provider.get_info()\n\n async def probe_model_multimodal(\n self,\n provider_id: str,\n model_id: str,\n image_only: bool = False,\n ) -> dict:\n \"\"\"Probe a model's multimodal capabilities and persist the result.\n\n Args:\n provider_id: Provider identifier.\n model_id: Model identifier.\n image_only: When True, skip the video probe for a faster result.\n Only ``supports_image`` will be accurate; ``supports_video``\n will remain at its previous value (not updated).\n \"\"\"\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n return {\"error\": f\"Provider '{provider_id}' not found\"}\n\n result = await provider.probe_model_multimodal(\n model_id,\n image_only=image_only,\n )\n\n # Update the model's capability flags.\n # For image_only probes, leave supports_video untouched so a\n # subsequent full probe can fill it in correctly.\n for model in provider.models + provider.extra_models:\n if model.id == model_id:\n model.supports_image = result.supports_image\n if not image_only:\n model.supports_video = result.supports_video\n model.supports_multimodal = result.supports_multimodal\n else:\n # Partial update: derive supports_multimodal from\n # image alone; video will be updated by the full probe.\n if result.supports_image:\n model.supports_multimodal = True\n model.probe_source = \"probed\"\n break\n\n # Compare probe result against expected baseline\n from .capability_baseline import (\n ExpectedCapabilityRegistry,\n compare_probe_result,\n )\n\n registry = ExpectedCapabilityRegistry()\n expected = registry.get_expected(provider_id, model_id)\n if expected:\n discrepancies = compare_probe_result(\n expected,\n result.supports_image,\n result.supports_video,\n )\n for d in discrepancies:\n logger.warning(\n \"Probe discrepancy: %s/%s %s expected=%s actual=%s (%s)\",\n d.provider_id,\n d.model_id,\n d.field,\n d.expected,\n d.actual,\n d.discrepancy_type,\n )\n\n # Persist to disk\n is_plugin = provider_id in self.plugin_providers\n if is_plugin:\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n self._save_plugin_provider(provider)\n else:\n self._save_provider(\n provider,\n is_builtin=provider_id in self.builtin_providers,\n )\n return {\n \"supports_image\": result.supports_image,\n \"supports_video\": result.supports_video,\n \"supports_multimodal\": result.supports_multimodal,\n \"image_message\": result.image_message,\n \"video_message\": result.video_message,\n }\n\n def _save_provider(\n self,\n provider: Provider,\n is_builtin: bool = False,\n skip_if_exists: bool = False,\n ):\n \"\"\"Save a provider configuration to disk.\n\n Sensitive fields (``api_key``) are encrypted before writing.\n \"\"\"\n provider_dir = self.builtin_path if is_builtin else self.custom_path\n provider_path = provider_dir / f\"{provider.id}.json\"\n if skip_if_exists and provider_path.exists():\n return\n data = encrypt_dict_fields(\n provider.model_dump(),\n PROVIDER_SECRET_FIELDS,\n )\n with open(provider_path, \"w\", encoding=\"utf-8\") as f:\n json.dump(data, f, ensure_ascii=False, indent=2)\n try:\n os.chmod(provider_path, 0o600)\n except OSError:\n pass\n\n def _save_plugin_provider(self, provider: Provider):\n \"\"\"Save a plugin provider configuration to disk.\n\n Sensitive fields (``api_key``) are encrypted before writing.\n \"\"\"\n provider_path = self.plugin_path / f\"{provider.id}.json\"\n data = encrypt_dict_fields(\n provider.model_dump(),\n PROVIDER_SECRET_FIELDS,\n )\n with open(provider_path, \"w\", encoding=\"utf-8\") as f:\n json.dump(data, f, ensure_ascii=False, indent=2)\n try:\n os.chmod(provider_path, 0o600)\n except OSError:\n pass\n\n def save_provider_config(\n self,\n provider_id: str,\n provider: Provider | None = None,\n ) -> None:\n \"\"\"Persist the current in-memory provider state to disk.\n\n Args:\n provider_id: The provider to save.\n provider: Optional pre-resolved provider instance. When\n supplied, this instance is saved directly — important\n for plugin providers where ``get_provider`` returns a\n fresh copy each time.\n \"\"\"\n if provider is None:\n provider = self.get_provider(provider_id)\n if provider is None:\n return\n is_plugin = provider_id in self.plugin_providers\n if is_plugin:\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n self._save_plugin_provider(provider)\n else:\n self._save_provider(\n provider,\n is_builtin=provider_id in self.builtin_providers,\n )\n\n def load_provider(\n self,\n provider_id: str,\n is_builtin: bool = False,\n ) -> Provider | None:\n \"\"\"Load a provider configuration from disk.\n\n Encrypted fields are transparently decrypted. If a legacy\n plaintext ``api_key`` is detected it is re-encrypted in place.\n \"\"\"\n provider_dir = self.builtin_path if is_builtin else self.custom_path\n provider_path = provider_dir / f\"{provider_id}.json\"\n if not provider_path.exists():\n return None\n try:\n with open(provider_path, \"r\", encoding=\"utf-8\") as f:\n data = json.load(f)\n\n needs_rewrite = self._maybe_migrate_plaintext(\n data,\n PROVIDER_SECRET_FIELDS,\n )\n data = decrypt_dict_fields(data, PROVIDER_SECRET_FIELDS)\n provider = self._provider_from_data(data)\n\n if needs_rewrite:\n try:\n self._save_provider(\n provider,\n is_builtin=is_builtin,\n skip_if_exists=False,\n )\n except Exception as enc_err:\n logger.debug(\n \"Deferred plaintext→encrypted migration\"\n \" for provider '%s': %s\",\n provider_id,\n enc_err,\n )\n\n return provider\n except Exception as e:\n logger.warning(\n \"Failed to load provider '%s' from %s: %s\",\n provider_id,\n provider_path,\n e,\n )\n return None\n\n @staticmethod\n def _maybe_migrate_plaintext(\n data: dict,\n secret_fields: frozenset[str],\n ) -> bool:\n \"\"\"Return ``True`` when *data* contains plaintext secret fields\n that should be re-encrypted on disk.\"\"\"\n for field in secret_fields:\n value = data.get(field)\n if isinstance(value, str) and value and not is_encrypted(value):\n return True\n return False\n\n # pylint: disable=too-many-return-statements\n def _provider_from_data(self, data: Dict) -> Provider:\n \"\"\"Deserialize provider data to a concrete provider type.\"\"\"\n provider_id = str(data.get(\"id\", \"\"))\n chat_model = str(data.get(\"chat_model\", \"\"))\n\n if provider_id == \"openrouter\":\n return OpenRouterProvider.model_validate(data)\n if provider_id == \"anthropic\" or chat_model == \"AnthropicChatModel\":\n return AnthropicProvider.model_validate(data)\n if provider_id == \"gemini\" or chat_model == \"GeminiChatModel\":\n return GeminiProvider.model_validate(data)\n if provider_id == \"dashscope\" or chat_model == \"DashScopeChatModel\":\n return DashScopeProvider.model_validate(data)\n if provider_id == \"ollama\":\n return OllamaProvider.model_validate(data)\n if chat_model == \"OpenAIResponseModel\":\n return OpenAIResponseProvider.model_validate(data)\n return OpenAIProvider.model_validate(data)\n\n def save_active_model(self, active_model: ModelSlotConfig):\n \"\"\"Save the active provider/model configuration to disk.\"\"\"\n active_path = self.root_path / \"active_model.json\"\n with open(active_path, \"w\", encoding=\"utf-8\") as f:\n json.dump(\n active_model.model_dump(),\n f,\n ensure_ascii=False,\n indent=2,\n )\n try:\n os.chmod(active_path, 0o600)\n except OSError:\n pass\n\n def clear_active_model(self, provider_id: str | None = None) -> bool:\n \"\"\"Clear the active provider/model configuration.\n\n If provider_id is provided, only clear when it matches the current\n active provider.\n \"\"\"\n if self.active_model is None:\n return False\n # Normalize provider ID for backward compatibility\n if provider_id is not None:\n provider_id = self._normalize_provider_id(provider_id)\n if (\n provider_id is not None\n and self.active_model.provider_id != provider_id\n ):\n return False\n\n self.active_model = None\n active_path = self.root_path / \"active_model.json\"\n try:\n active_path.unlink()\n except (FileNotFoundError, OSError):\n pass\n return True\n\n def load_active_model(self) -> ModelSlotConfig | None:\n \"\"\"Load the active provider/model configuration from disk.\"\"\"\n active_path = self.root_path / \"active_model.json\"\n if not active_path.exists():\n return None\n try:\n with open(active_path, \"r\", encoding=\"utf-8\") as f:\n data = json.load(f)\n return ModelSlotConfig.model_validate(data)\n except Exception:\n return None\n\n def _migrate_copaw_config(self) -> None:\n \"\"\"Migrate copaw-local provider config to qwenpaw-local.\"\"\"\n # 1. Migrate active model configuration (only provider_id)\n if (\n self.active_model\n and self.active_model.provider_id == \"copaw-local\"\n ):\n self.active_model.provider_id = \"qwenpaw-local\"\n self.save_active_model(self.active_model)\n logger.info(\n \"Migrated active model provider from \"\n \"'copaw-local' to 'qwenpaw-local'\",\n )\n\n # 2. Migrate stored provider config file\n copaw_config_path = self.builtin_path / \"copaw-local.json\"\n if not copaw_config_path.exists():\n return\n\n try:\n # Load old config and apply to new provider instance\n with open(copaw_config_path, \"r\", encoding=\"utf-8\") as f:\n old_config = json.load(f)\n\n # Get the new built-in provider instance\n provider = self.builtin_providers.get(\"qwenpaw-local\")\n if not provider:\n return\n\n # Apply migrated configuration (preserve extra_models as-is)\n if \"extra_models\" in old_config:\n provider.extra_models = [\n ModelInfo.model_validate(model)\n for model in old_config[\"extra_models\"]\n ]\n if \"base_url\" in old_config:\n provider.base_url = old_config[\"base_url\"]\n if \"generate_kwargs\" in old_config:\n provider.generate_kwargs = old_config[\"generate_kwargs\"]\n\n # Save using standard persistence logic (with encryption)\n self._save_provider(provider, is_builtin=True)\n\n # Remove old config file\n copaw_config_path.unlink()\n logger.info(\n \"Migrated provider config from \"\n \"'copaw-local.json' to 'qwenpaw-local.json'\",\n )\n except Exception as exc:\n logger.warning(\"Failed to migrate copaw-local config: %s\", exc)\n\n # pylint: disable=too-many-branches\n def _migrate_legacy_providers(self):\n \"\"\"Migrate from legacy providers.json format to the new structure.\"\"\"\n legacy_path = SECRET_DIR / \"providers.json\"\n if legacy_path.exists() and legacy_path.is_file():\n with open(legacy_path, \"r\", encoding=\"utf-8\") as f:\n legacy_data = json.load(f)\n builtin_providers = legacy_data.get(\"providers\", {})\n custom_providers = legacy_data.get(\"custom_providers\", {})\n active_model = legacy_data.get(\"active_llm\", {})\n # Migrate built-in providers\n for provider_id, config in builtin_providers.items():\n provider = self.get_provider(provider_id)\n if not provider:\n logger.warning(\n \"Legacy provider '%s' not found in\"\n \" registry, skipping migration for this provider.\",\n provider_id,\n )\n continue\n if \"api_key\" in config:\n provider.api_key = config[\"api_key\"]\n if \"extra_models\" in config:\n provider.extra_models = [\n ModelInfo.model_validate(model)\n for model in config[\"extra_models\"]\n ]\n if not provider.freeze_url and \"base_url\" in config:\n provider.base_url = config[\"base_url\"]\n self._save_provider(provider, is_builtin=True)\n # Migrate custom providers\n for provider_id, data in custom_providers.items():\n custom_provider = OpenAIProvider(\n id=provider_id,\n name=data.get(\"name\", provider_id),\n base_url=data.get(\"base_url\", \"\"),\n api_key=data.get(\"api_key\", \"\"),\n is_custom=True,\n )\n if \"models\" in data:\n # migrate models to extra_models field\n custom_provider.extra_models = [\n ModelInfo.model_validate(model)\n for model in data[\"models\"]\n ]\n if \"chat_model\" in data:\n custom_provider.chat_model = data[\"chat_model\"]\n self._save_provider(custom_provider, is_builtin=False)\n # Migrate active model (only provider_id, not model)\n if active_model:\n try:\n # Convert legacy copaw-local provider_id\n if active_model.get(\"provider_id\") == \"copaw-local\":\n active_model[\"provider_id\"] = \"qwenpaw-local\"\n self.active_model = ModelSlotConfig.model_validate(\n active_model,\n )\n self.save_active_model(self.active_model)\n except Exception:\n logger.warning(\n \"Failed to migrate active model, using default.\",\n )\n # Remove legacy file after migration\n try:\n os.remove(legacy_path)\n except Exception:\n logger.warning(\n \"Failed to remove legacy providers.json after migration.\",\n )\n\n @staticmethod\n def _restore_builtin_model_config(\n model: ModelInfo,\n config: dict,\n ) -> None:\n \"\"\"Restore persisted overrides onto a built-in model definition.\"\"\"\n if config[\"generate_kwargs\"]:\n model.generate_kwargs = config[\"generate_kwargs\"]\n if config[\"max_tokens\"] is not None:\n model.max_tokens = config[\"max_tokens\"]\n if config[\"max_input_length\"] is not None:\n model.max_input_length = config[\"max_input_length\"]\n configured_length = config.get(\"max_input_length\")\n model.max_input_length_configured = bool(\n config.get(\"max_input_length_configured\", False)\n or (\n configured_length is not None\n and configured_length != DEFAULT_CONTEXT_WINDOW\n ),\n )\n if config.get(\"relay_reasoning\") is not None:\n model.relay_reasoning = config[\"relay_reasoning\"]\n if config.get(\"thinking_enabled\") is not None:\n model.thinking_enabled = config[\"thinking_enabled\"]\n if config.get(\"thinking_budget\") is not None:\n model.thinking_budget = config[\"thinking_budget\"]\n if config.get(\"reasoning_effort\") is not None:\n model.reasoning_effort = config[\"reasoning_effort\"]\n if config.get(\"supports_multimodal\") is not None:\n model.supports_multimodal = config[\"supports_multimodal\"]\n if config.get(\"supports_image\") is not None:\n model.supports_image = config[\"supports_image\"]\n if config.get(\"supports_video\") is not None:\n model.supports_video = config[\"supports_video\"]\n\n def _init_from_storage(self):\n \"\"\"Initialize all providers and active model from disk storage.\"\"\"\n # Load built-in providers\n # pylint: disable=too-many-nested-blocks\n for builtin in self.builtin_providers.values():\n provider = self.load_provider(builtin.id, is_builtin=True)\n if provider:\n # inherit user-configured base_url only when freeze_url=False\n if not builtin.freeze_url:\n builtin.base_url = provider.base_url\n builtin.api_key = provider.api_key\n if provider.auth_mode != \"api_key\":\n builtin.auth_mode = provider.auth_mode\n if provider.custom_headers:\n builtin.custom_headers = provider.custom_headers\n # Restore the configurable inline-media cap for the providers\n # that support it (currently DashScope).\n if hasattr(builtin, \"max_inline_media_bytes\"):\n builtin.max_inline_media_bytes = (\n provider.max_inline_media_bytes\n )\n builtin_model_ids = {m.id for m in builtin.models}\n builtin.extra_models = [\n m\n for m in provider.extra_models\n if m.id not in builtin_model_ids\n ]\n builtin.generate_kwargs.update(provider.generate_kwargs)\n # Restore per-model config for built-in models.\n # Collect from both stored built-in models and promoted\n # extra_models (models that were user-added but are now\n # part of the built-in list).\n stored_model_config: dict = {}\n _per_model_keys = (\n \"generate_kwargs\",\n \"max_tokens\",\n \"max_input_length\",\n \"max_input_length_configured\",\n \"relay_reasoning\",\n \"thinking_enabled\",\n \"thinking_budget\",\n \"reasoning_effort\",\n \"supports_multimodal\",\n \"supports_image\",\n \"supports_video\",\n )\n for m in provider.models:\n stored_model_config[m.id] = {\n k: getattr(m, k) for k in _per_model_keys\n }\n for m in provider.extra_models:\n if m.id in builtin_model_ids:\n stored_model_config.setdefault(\n m.id,\n {k: getattr(m, k) for k in _per_model_keys},\n )\n if stored_model_config:\n for model in builtin.models:\n cfg = stored_model_config.get(model.id)\n if cfg:\n self._restore_builtin_model_config(model, cfg)\n # Load custom providers\n for provider_file in self.custom_path.glob(\"*.json\"):\n provider = self.load_provider(provider_file.stem, is_builtin=False)\n if provider:\n self.custom_providers[provider.id] = provider\n # Load active model config\n active_model = self.load_active_model()\n if active_model:\n self.active_model = active_model\n\n # Migrate copaw-local to qwenpaw-local for backwards compatibility\n self._migrate_copaw_config()\n\n def _apply_default_annotations(self):\n \"\"\"Apply doc-based default annotations for unprobed models.\n\n Models that already carry static annotations (supports_image /\n supports_video set at definition time) only need the derived\n supports_multimodal flag computed. Models with no annotations\n at all fall back to the ExpectedCapabilityRegistry.\n \"\"\"\n from .capability_baseline import ExpectedCapabilityRegistry\n\n registry = ExpectedCapabilityRegistry()\n for provider in self.builtin_providers.values():\n for model in provider.models:\n # Already fully annotated (e.g. by a prior probe) → skip\n if model.supports_multimodal is not None:\n continue\n\n # Static annotations present → compute derived flag only\n if (\n model.supports_image is not None\n or model.supports_video is not None\n ):\n model.supports_multimodal = bool(\n model.supports_image or model.supports_video,\n )\n continue\n\n # No annotations at all → fall back to registry\n expected = registry.get_expected(provider.id, model.id)\n if expected:\n model.supports_image = expected.expected_image\n model.supports_video = expected.expected_video\n model.supports_multimodal = bool(\n expected.expected_image or expected.expected_video,\n )\n model.probe_source = \"documentation\"\n\n async def _resume_local_model(self, local_manager) -> None:\n \"\"\"Resume the active local model server from the previous run.\"\"\"\n\n def _clear_local_provider():\n self.update_provider(\n \"qwenpaw-local\",\n {\n \"base_url\": \"\",\n \"extra_models\": [],\n },\n )\n\n local_models = self.get_provider(\"qwenpaw-local\").extra_models\n model_id = local_models[0].id if local_models else None\n if model_id is None:\n return\n\n installed, _ = local_manager.check_llamacpp_installation()\n if not installed:\n logger.info(\n \"Skipping local model restore because\"\n \" llama.cpp is not installed.\",\n )\n _clear_local_provider()\n return\n\n if not local_manager.is_model_downloaded(model_id):\n logger.warning(\n \"Skipping local model restore because\"\n \" model is not downloaded: %s\",\n model_id,\n )\n _clear_local_provider()\n return\n\n try:\n setup_result = await local_manager.setup_server(model_id)\n except (FileNotFoundError, RuntimeError, ValueError) as exc:\n logger.warning(\n \"Failed to restore local model server for %s: %s\",\n model_id,\n exc,\n )\n _clear_local_provider()\n return\n\n self.update_provider(\n \"qwenpaw-local\",\n {\n \"base_url\": f\"http://127.0.0.1:{setup_result.port}/v1\",\n \"extra_models\": [setup_result.model_info],\n },\n )\n\n def register_plugin_provider(\n self,\n provider_id: str,\n provider_class,\n label: str,\n base_url: str,\n metadata: Dict,\n ):\n \"\"\"Register a plugin provider.\n\n Args:\n provider_id: Provider ID\n provider_class: Provider class\n label: Display label\n base_url: API base URL\n metadata: Additional metadata\n \"\"\"\n # Get default models from provider class if available\n default_models = []\n if hasattr(provider_class, \"get_default_models\"):\n try:\n default_models = provider_class.get_default_models()\n except Exception as e:\n logger.warning(\n f\"Failed to get default models for {provider_id}: {e}\",\n )\n\n # Create ProviderInfo\n provider_info = ProviderInfo(\n id=provider_id,\n name=label,\n base_url=base_url,\n api_key=\"\", # Will be configured by user\n chat_model=metadata.get(\"chat_model\", \"OpenAIChatModel\"),\n models=default_models, # Add default models\n is_custom=False, # Mark as non-custom (like builtin, cannot be\n # deleted)\n require_api_key=metadata.get(\"require_api_key\", True),\n meta=metadata.get(\"meta\", {}), # Pass meta from plugin\n )\n\n # Check if there's a saved configuration for this plugin provider\n saved_config_path = self.plugin_path / f\"{provider_id}.json\"\n if saved_config_path.exists():\n try:\n with open(saved_config_path, \"r\", encoding=\"utf-8\") as f:\n saved_config = json.load(f)\n\n # Decrypt sensitive fields\n saved_config = decrypt_dict_fields(\n saved_config,\n PROVIDER_SECRET_FIELDS,\n )\n\n # Merge saved config (api_key, base_url, extra_models, etc)\n if \"api_key\" in saved_config:\n provider_info.api_key = saved_config[\"api_key\"]\n if \"base_url\" in saved_config:\n provider_info.base_url = saved_config[\"base_url\"]\n if \"generate_kwargs\" in saved_config:\n provider_info.generate_kwargs = saved_config[\n \"generate_kwargs\"\n ]\n # Load extra_models from saved config\n if \"extra_models\" in saved_config:\n provider_info.extra_models = [\n ModelInfo.model_validate(\n model.model_dump()\n if isinstance(model, BaseModel)\n else model,\n )\n for model in saved_config[\"extra_models\"]\n ]\n logger.info(\n f\"✓ Loaded saved config for plugin provider: \"\n f\"{provider_id} \"\n f\"({len(provider_info.extra_models)} extra model(s))\",\n )\n except Exception as e:\n logger.warning(\n f\"Failed to load saved config for {provider_id}: {e}\",\n )\n\n # Register to internal dict\n self.plugin_providers[provider_id] = {\n \"info\": provider_info,\n \"class\": provider_class,\n }\n\n logger.info(\n f\"✓ Registered plugin provider: {provider_id} \"\n f\"with {len(default_models)} default model(s)\",\n )\n\n def unregister_plugin_provider(self, provider_id: str) -> bool:\n \"\"\"Remove a plugin provider from memory.\n\n Removes the provider from ``self.plugin_providers`` so it no\n longer appears in the model list. The persisted configuration\n file (``plugin_path/{provider_id}.json``) is intentionally\n kept on disk so that user-configured keys survive a\n reinstall.\n\n Args:\n provider_id: Plugin provider identifier to remove.\n\n Returns:\n ``True`` if the provider was found and removed,\n ``False`` if it was not registered.\n \"\"\"\n if provider_id not in self.plugin_providers:\n logger.warning(\n f\"unregister_plugin_provider: '{provider_id}' not found\",\n )\n return False\n del self.plugin_providers[provider_id]\n logger.info(\n f\"Unregistered plugin provider '{provider_id}' from memory\",\n )\n return True\n\n @staticmethod\n def get_instance() -> \"ProviderManager\":\n \"\"\"Get the singleton instance of ProviderManager.\"\"\"\n if ProviderManager._instance is None:\n ProviderManager._instance = ProviderManager()\n return ProviderManager._instance\n\n @staticmethod\n def get_active_chat_model() -> ChatModelBase:\n \"\"\"Get the currently active provider/model configuration.\"\"\"\n manager = ProviderManager.get_instance()\n model = manager.get_active_model()\n if model is None or model.provider_id == \"\" or model.model == \"\":\n raise ProviderError(\n message=\"No active model configured.\",\n )\n provider = manager.get_provider(model.provider_id)\n if provider is None:\n raise ProviderError(\n message=f\"Active provider '{model.provider_id}' not found.\",\n )\n return provider.get_chat_model_instance(model.model)\n"}, "db8fb6391097791b08f63623ba4cd88421ec5cdb": {"t": 1785846835.9702632, "n": 83823, "h": "ed2bd12a6def38a9", "c": "# -*- coding: utf-8 -*-\n\"\"\"A Manager class to handle all providers, including built-in and custom ones.\nIt provides a unified interface to manage providers, such as listing available\nproviders, adding/removing custom providers, and fetching provider details.\"\"\"\n\nimport asyncio\nimport json\nimport logging\nimport os\nfrom typing import Dict, List\n\nfrom agentscope.model import ChatModelBase\nfrom pydantic import BaseModel\n\nfrom qwenpaw.exceptions import ModelNotFoundException\n\nfrom ..config.config import ModelSlotConfig\nfrom ..constant import SECRET_DIR\nfrom ..exceptions import ProviderError\nfrom ..security.secret_store import (\n PROVIDER_SECRET_FIELDS,\n decrypt_dict_fields,\n encrypt_dict_fields,\n is_encrypted,\n)\nfrom .anthropic_provider import AnthropicProvider\nfrom .context_windows import DEFAULT_CONTEXT_WINDOW\nfrom .dashscope_provider import DashScopeProvider\nfrom .gemini_provider import GeminiProvider\nfrom .lmstudio_provider import LMStudioProvider\nfrom .ollama_provider import OllamaProvider\nfrom .openai_provider import (\n GitHubModelsProvider,\n KiloProvider,\n OpenAIProvider,\n OpenCodeProvider,\n)\nfrom .openai_response_provider import OpenAIResponseProvider\nfrom .openrouter_provider import OpenRouterProvider\nfrom .provider import ModelInfo, Provider, ProviderInfo\n\nlogger = logging.getLogger(__name__)\n\n# -------------------------------------------------------\n# Built-in provider definitions and their default models.\n# -------------------------------------------------------\n\nMODELSCOPE_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"Qwen/Qwen3.5-122B-A10B\",\n name=\"Qwen3.5-122B-A10B\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"ZhipuAI/GLM-5\",\n name=\"GLM-5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nDASHSCOPE_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"qwen3.7-max\",\n name=\"Qwen3.7 Max\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n thinking_enabled=True,\n relay_reasoning=False,\n ),\n ModelInfo(\n id=\"qwen3.7-plus\",\n name=\"Qwen3.7 Plus\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n thinking_enabled=True,\n relay_reasoning=False,\n ),\n ModelInfo(\n id=\"qwen3.6-plus\",\n name=\"Qwen3.6 Plus\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n thinking_enabled=True,\n relay_reasoning=False,\n ),\n ModelInfo(\n id=\"deepseek-v4-pro\",\n name=\"DeepSeek-V4 Pro\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n thinking_enabled=True,\n thinking_param_style=\"effort\",\n reasoning_effort_options=[\"high\", \"max\"],\n relay_reasoning=False,\n ),\n ModelInfo(\n id=\"glm-5.2\",\n name=\"GLM-5.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n thinking_enabled=True,\n thinking_param_style=\"effort\",\n reasoning_effort_options=[\"high\", \"max\"],\n relay_reasoning=False,\n ),\n]\n\nMIMO_TOKENPLAN_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"mimo-v2.5-pro\",\n name=\"MiMo V2.5 Pro\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"mimo-v2.5\",\n name=\"MiMo V2.5\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n]\n\nALIYUN_TOKENPLAN_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"qwen3.7-plus\",\n name=\"Qwen3.7 Plus\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3.7-max\",\n name=\"Qwen3.7 Max\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3.6-plus\",\n name=\"Qwen3.6 Plus\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3.6-flash\",\n name=\"Qwen3.6 Flash\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-v4-pro\",\n name=\"DeepSeek-V4 Pro\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-v4-flash\",\n name=\"DeepSeek-V4 Flash\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-v3.2\",\n name=\"DeepSeek-V3.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5.2\",\n name=\"GLM-5.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5.1\",\n name=\"GLM-5.1\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5\",\n name=\"GLM-5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.5\",\n name=\"MiniMax M2.5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2.6\",\n name=\"Kimi K2.6\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2.5\",\n name=\"Kimi K2.5\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n]\n\nALIYUN_CODINGPLAN_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"qwen3.6-plus\",\n name=\"Qwen3.6 Plus\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3.5-plus\",\n name=\"Qwen3.5 Plus\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5.2\",\n name=\"GLM-5.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5.1\",\n name=\"GLM-5.1\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5\",\n name=\"GLM-5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-4.7\",\n name=\"GLM-4.7\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.5\",\n name=\"MiniMax M2.5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2.5\",\n name=\"Kimi K2.5\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3-max-2026-01-23\",\n name=\"Qwen3 Max 2026-01-23\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3-coder-next\",\n name=\"Qwen3 Coder Next\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3-coder-plus\",\n name=\"Qwen3 Coder Plus\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nZHIPU_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"glm-4.7-flash\",\n name=\"GLM-4.7-Flash\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"glm-5\",\n name=\"glm-5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5.2\",\n name=\"glm-5.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5.1\",\n name=\"glm-5.1\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5-turbo\",\n name=\"glm-5-turbo\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5v-turbo\",\n name=\"glm-5v-turbo\",\n supports_image=True,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nOPENAI_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"gpt-5.2\",\n name=\"GPT-5.2\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-5\",\n name=\"GPT-5\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-5-mini\",\n name=\"GPT-5 Mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-5-nano\",\n name=\"GPT-5 Nano\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4.1\",\n name=\"GPT-4.1\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4.1-mini\",\n name=\"GPT-4.1 Mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4.1-nano\",\n name=\"GPT-4.1 Nano\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"o3\",\n name=\"o3\",\n supports_image=True,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"o4-mini\",\n name=\"o4-mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4o\",\n name=\"GPT-4o\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4o-mini\",\n name=\"GPT-4o Mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n]\n\nKILO_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"kilo-auto/free\",\n name=\"Kilo Auto (Free Router)\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"nvidia/nemotron-3-ultra-550b-a55b:free\",\n name=\"Nemotron 3 Ultra 550B\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"nvidia/nemotron-3-super-120b-a12b:free\",\n name=\"Nemotron 3 Super 120B\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"poolside/laguna-m.1:free\",\n name=\"Poolside Laguna M.1\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"poolside/laguna-xs.2:free\",\n name=\"Poolside Laguna XS.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"stepfun/step-3.7-flash:free\",\n name=\"Step 3.7 Flash\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"nex-agi/nex-n2-pro:free\",\n name=\"Nex N2 Pro\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n]\n\nOPENCODE_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"deepseek-v4-flash-free\",\n name=\"DeepSeek V4 Flash\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"mimo-v2.5-free\",\n name=\"Mimo V2.5\",\n supports_image=True,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"nemotron-3-ultra-free\",\n name=\"Nemotron 3 Ultra\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"nemotron-3-super-free\",\n name=\"Nemotron 3 Super\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n]\n\nAZURE_OPENAI_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"gpt-5-chat\",\n name=\"GPT-5 Chat\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-5-mini\",\n name=\"GPT-5 Mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-5-nano\",\n name=\"GPT-5 Nano\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4.1\",\n name=\"GPT-4.1\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4.1-mini\",\n name=\"GPT-4.1 Mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4.1-nano\",\n name=\"GPT-4.1 Nano\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4o\",\n name=\"GPT-4o\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4o-mini\",\n name=\"GPT-4o Mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n]\n\nMINIMAX_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"MiniMax-M3\",\n name=\"MiniMax M3\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.7\",\n name=\"MiniMax M2.7\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.7-highspeed\",\n name=\"MiniMax M2.7 Highspeed\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.5\",\n name=\"MiniMax M2.5 (legacy)\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.5-highspeed\",\n name=\"MiniMax M2.5 Highspeed (legacy)\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.1\",\n name=\"MiniMax M2.1 (legacy)\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.1-highspeed\",\n name=\"MiniMax M2.1 Highspeed (legacy)\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2\",\n name=\"MiniMax M2 (legacy)\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nKIMI_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"kimi-k2.5\",\n name=\"Kimi K2.5\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2-0905-preview\",\n name=\"Kimi K2 0905 Preview\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2-0711-preview\",\n name=\"Kimi K2 0711 Preview\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2-turbo-preview\",\n name=\"Kimi K2 Turbo Preview\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2-thinking\",\n name=\"Kimi K2 Thinking\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2-thinking-turbo\",\n name=\"Kimi K2 Thinking Turbo\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nDEEPSEEK_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"deepseek-chat\",\n name=\"DeepSeek Chat\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-reasoner\",\n name=\"DeepSeek Reasoner\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-v4-flash\",\n name=\"DeepSeek V4 Flash\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-v4-pro\",\n name=\"DeepSeek V4 Pro\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nVOLCENGINE_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"doubao-seed-2-0-code-preview-260215\",\n name=\"Doubao-Seed-2.0-Code\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-2-0-pro-260215\",\n name=\"Doubao-Seed-2.0-pro\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-2-0-lite-260428\",\n name=\"Doubao-Seed-2.0-lite\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-code-preview-251028\",\n name=\"Doubao-Seed-Code\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"glm-4-7-251222\",\n name=\"GLM-4.7\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-v3-2-251201\",\n name=\"DeepSeek-V3.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"doubao-seed-1-8-251228\",\n name=\"Doubao-Seed-1.8\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-2-0-mini-260428\",\n name=\"Doubao-Seed-2.0-mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-character-251128\",\n name=\"Doubao-Seed-Character\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nVOLCENGINE_CODINGPLAN_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"doubao-seed-2-0-code-preview-260215\",\n name=\"Doubao-Seed-2.0-Code\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-2-0-pro-260215\",\n name=\"Doubao-Seed-2.0-pro\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-2-0-lite-260428\",\n name=\"Doubao-Seed-2.0-lite\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-code-preview-251028\",\n name=\"Doubao-Seed-Code\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"glm-5.1\",\n name=\"GLM-5.1\",\n supports_image=False,\n supports_video=False,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"minimax-m2.7\",\n name=\"MiniMax-M2.7\",\n supports_image=False,\n supports_video=False,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"kimi-k2.6\",\n name=\"Kimi-K2.6\",\n supports_image=False,\n supports_video=False,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"kimi-k2.5\",\n name=\"Kimi-K2.5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"glm-4-7-251222\",\n name=\"GLM-4.7\",\n supports_image=False,\n supports_video=False,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"deepseek-v3-2-251201\",\n name=\"DeepSeek-V3.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"probed\",\n ),\n]\n\nANTHROPIC_MODELS: List[ModelInfo] = []\n\nGEMINI_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"gemini-3.1-pro-preview\",\n name=\"Gemini 3.1 Pro Preview\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gemini-3-flash-preview\",\n name=\"Gemini 3 Flash Preview\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gemini-3.1-flash-lite-preview\",\n name=\"Gemini 3.1 Flash Lite Preview\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gemini-2.5-pro\",\n name=\"Gemini 2.5 Pro\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gemini-2.5-flash\",\n name=\"Gemini 2.5 Flash\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gemini-2.5-flash-lite\",\n name=\"Gemini 2.5 Flash Lite\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gemini-2.0-flash\",\n name=\"Gemini 2.0 Flash\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n]\n\nPROVIDER_MODELSCOPE = OpenAIProvider(\n id=\"modelscope\",\n name=\"ModelScope\",\n base_url=\"https://api-inference.modelscope.cn/v1\",\n api_key_prefix=\"ms\",\n models=MODELSCOPE_MODELS,\n freeze_url=True,\n)\n\nPROVIDER_DASHSCOPE = DashScopeProvider(\n id=\"dashscope\",\n name=\"DashScope\",\n base_url=\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n api_key_prefix=\"sk\",\n models=DASHSCOPE_MODELS,\n provider_group=\"aliyun\",\n provider_group_name=\"Aliyun\",\n provider_variant=\"dashscope\",\n meta={\n \"base_url_options\": [\n {\n \"label\": \"China (Beijing)\",\n \"value\": \"https://dashscope.aliyuncs.com/\"\n \"compatible-mode/v1\",\n },\n {\n \"label\": \"International (Singapore)\",\n \"value\": \"https://dashscope-intl.aliyuncs.com/\"\n \"compatible-mode/v1\",\n },\n {\n \"label\": \"US (Virginia)\",\n \"value\": \"https://dashscope-us.aliyuncs.com/\"\n \"compatible-mode/v1\",\n },\n ],\n },\n)\n\nPROVIDER_ALIYUN_CODINGPLAN = OpenAIProvider(\n id=\"aliyun-codingplan\",\n name=\"Aliyun Coding Plan (China)\",\n base_url=\"https://coding.dashscope.aliyuncs.com/v1\",\n api_key_prefix=\"sk-sp\",\n models=ALIYUN_CODINGPLAN_MODELS,\n support_connection_check=False,\n freeze_url=True,\n provider_group=\"aliyun\",\n provider_group_name=\"Aliyun\",\n provider_variant=\"coding_plan_cn\",\n)\n\nPROVIDER_ALIYUN_CODINGPLAN_INTL = OpenAIProvider(\n id=\"aliyun-codingplan-intl\",\n name=\"Aliyun Coding Plan (International)\",\n base_url=\"https://coding-intl.dashscope.aliyuncs.com/v1\",\n api_key_prefix=\"sk-sp\",\n models=ALIYUN_CODINGPLAN_MODELS,\n support_connection_check=False,\n freeze_url=True,\n provider_group=\"aliyun\",\n provider_group_name=\"Aliyun\",\n provider_variant=\"coding_plan_intl\",\n)\n\nPROVIDER_ALIYUN_TOKENPLAN = OpenAIProvider(\n id=\"aliyun-tokenplan\",\n name=\"Aliyun Token Plan\",\n base_url=(\n \"https://token-plan.cn-beijing.maas.aliyuncs.com/\" \"compatible-mode/v1\"\n ),\n api_key_prefix=\"sk-sp\",\n models=ALIYUN_TOKENPLAN_MODELS,\n support_connection_check=False,\n freeze_url=True,\n provider_group=\"aliyun\",\n provider_group_name=\"Aliyun\",\n provider_variant=\"token_plan\",\n)\n\nPROVIDER_ALIYUN_TOKENPLAN_INTL = OpenAIProvider(\n id=\"aliyun-tokenplan-intl\",\n name=\"Aliyun Token Plan (International)\",\n base_url=(\n \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/\"\n \"compatible-mode/v1\"\n ),\n api_key_prefix=\"sk-sp\",\n models=ALIYUN_TOKENPLAN_MODELS,\n support_connection_check=False,\n freeze_url=True,\n provider_group=\"aliyun\",\n provider_group_name=\"Aliyun\",\n provider_variant=\"token_plan_intl\",\n)\n\nPROVIDER_ZHIPU_CN = OpenAIProvider(\n id=\"zhipu-cn\",\n name=\"Zhipu (BigModel)\",\n base_url=\"https://open.bigmodel.cn/api/paas/v4\",\n api_key_prefix=\"\",\n models=ZHIPU_MODELS,\n freeze_url=True,\n provider_group=\"zhipu\",\n provider_group_name=\"Zhipu\",\n provider_variant=\"open_platform_cn\",\n meta={\"is_free_tier\": True},\n)\n\nPROVIDER_ZHIPU_CN_CODINGPLAN = OpenAIProvider(\n id=\"zhipu-cn-codingplan\",\n name=\"Zhipu Coding Plan (BigModel)\",\n base_url=\"https://open.bigmodel.cn/api/coding/paas/v4\",\n api_key_prefix=\"\",\n models=ZHIPU_MODELS,\n freeze_url=True,\n support_connection_check=False,\n provider_group=\"zhipu\",\n provider_group_name=\"Zhipu\",\n provider_variant=\"coding_plan_cn\",\n)\n\nPROVIDER_ZHIPU_INTL = OpenAIProvider(\n id=\"zhipu-intl\",\n name=\"Zhipu (Z.AI)\",\n base_url=\"https://api.z.ai/api/paas/v4\",\n api_key_prefix=\"\",\n models=ZHIPU_MODELS,\n freeze_url=True,\n provider_group=\"zhipu\",\n provider_group_name=\"Zhipu\",\n provider_variant=\"open_platform_intl\",\n)\n\nPROVIDER_ZHIPU_INTL_CODINGPLAN = OpenAIProvider(\n id=\"zhipu-intl-codingplan\",\n name=\"Zhipu Coding Plan (Z.AI)\",\n base_url=\"https://api.z.ai/api/coding/paas/v4\",\n api_key_prefix=\"\",\n models=ZHIPU_MODELS,\n freeze_url=True,\n support_connection_check=False,\n provider_group=\"zhipu\",\n provider_group_name=\"Zhipu\",\n provider_variant=\"coding_plan_intl\",\n)\n\nPROVIDER_QWENPAW = OpenAIProvider(\n id=\"qwenpaw-local\",\n name=\"QwenPaw Local\",\n is_local=True,\n require_api_key=False,\n)\n\nPROVIDER_OPENAI = OpenAIProvider(\n id=\"openai\",\n name=\"OpenAI\",\n base_url=\"https://api.openai.com/v1\",\n api_key_prefix=\"sk-\",\n models=OPENAI_MODELS,\n freeze_url=True,\n)\n\nPROVIDER_OPENAI_RESPONSE = OpenAIResponseProvider(\n id=\"openai-response\",\n name=\"OpenAI (Response API)\",\n base_url=\"https://api.openai.com/v1\",\n api_key_prefix=\"sk-\",\n chat_model=\"OpenAIResponseModel\",\n models=OPENAI_MODELS,\n freeze_url=True,\n)\n\nPROVIDER_OPENCODE = OpenCodeProvider(\n id=\"opencode\",\n name=\"OpenCode\",\n base_url=\"https://opencode.ai/zen/v1\",\n api_key_prefix=\"\",\n models=OPENCODE_MODELS,\n require_api_key=False,\n meta={\n \"base_url_options\": [\n {\"label\": \"OpenCode\", \"value\": \"https://opencode.ai/zen/v1\"},\n {\"label\": \"OpenCode Go\", \"value\": \"https://opencode.ai/zen/go/v1\"},\n ],\n \"is_free_tier\": True,\n },\n freeze_url=False,\n)\n\nPROVIDER_KILO = KiloProvider(\n id=\"kilo\",\n name=\"Kilo Code\",\n base_url=\"https://api.kilo.ai/api/gateway\",\n api_key_prefix=\"\",\n models=KILO_MODELS,\n require_api_key=False,\n meta={\"is_free_tier\": True},\n freeze_url=True,\n)\n\nPROVIDER_AZURE_OPENAI = OpenAIProvider(\n id=\"azure-openai\",\n name=\"Azure OpenAI\",\n api_key_prefix=\"\",\n models=AZURE_OPENAI_MODELS,\n)\n\nPROVIDER_MINIMAX = AnthropicProvider(\n id=\"minimax\",\n name=\"MiniMax (International)\",\n base_url=\"https://api.minimax.io/anthropic\",\n models=MINIMAX_MODELS,\n chat_model=\"AnthropicChatModel\",\n freeze_url=True,\n support_connection_check=False,\n provider_group=\"minimax\",\n provider_group_name=\"MiniMax\",\n provider_variant=\"open_platform_intl\",\n)\n\nPROVIDER_MINIMAX_CN = AnthropicProvider(\n id=\"minimax-cn\",\n name=\"MiniMax (China)\",\n base_url=\"https://api.minimaxi.com/anthropic\",\n models=MINIMAX_MODELS,\n chat_model=\"AnthropicChatModel\",\n freeze_url=True,\n support_connection_check=False,\n provider_group=\"minimax\",\n provider_group_name=\"MiniMax\",\n provider_variant=\"open_platform_cn\",\n)\n\nPROVIDER_KIMI_CN = OpenAIProvider(\n id=\"kimi-cn\",\n name=\"Kimi (China)\",\n base_url=\"https://api.moonshot.cn/v1\",\n api_key_prefix=\"\",\n models=KIMI_MODELS,\n freeze_url=True,\n provider_group=\"kimi\",\n provider_group_name=\"Kimi\",\n provider_variant=\"open_platform_cn\",\n)\n\nPROVIDER_KIMI_INTL = OpenAIProvider(\n id=\"kimi-intl\",\n name=\"Kimi (International)\",\n base_url=\"https://api.moonshot.ai/v1\",\n api_key_prefix=\"\",\n models=KIMI_MODELS,\n freeze_url=True,\n provider_group=\"kimi\",\n provider_group_name=\"Kimi\",\n provider_variant=\"open_platform_intl\",\n)\n\nKIMI_CODINGPLAN_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"kimi-for-coding\",\n name=\"Kimi for Coding\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nPROVIDER_KIMI_CODINGPLAN = OpenAIProvider(\n id=\"kimi-codingplan\",\n name=\"Kimi Coding Plan\",\n base_url=\"https://api.kimi.com/coding/v1\",\n api_key_prefix=\"sk-kimi-\",\n models=KIMI_CODINGPLAN_MODELS,\n freeze_url=True,\n support_connection_check=False,\n provider_group=\"kimi\",\n provider_group_name=\"Kimi\",\n provider_variant=\"coding_plan\",\n)\n\nPROVIDER_DEEPSEEK = OpenAIProvider(\n id=\"deepseek\",\n name=\"DeepSeek\",\n base_url=\"https://api.deepseek.com\",\n api_key_prefix=\"sk-\",\n models=DEEPSEEK_MODELS,\n freeze_url=True,\n)\n\nPROVIDER_ANTHROPIC = AnthropicProvider(\n id=\"anthropic\",\n name=\"Anthropic\",\n base_url=\"https://api.anthropic.com\",\n api_key_prefix=\"sk-ant-\",\n models=ANTHROPIC_MODELS,\n chat_model=\"AnthropicChatModel\",\n freeze_url=False,\n)\n\nPROVIDER_GEMINI = GeminiProvider(\n id=\"gemini\",\n name=\"Google Gemini\",\n base_url=\"https://generativelanguage.googleapis.com\",\n api_key_prefix=\"\",\n models=GEMINI_MODELS,\n chat_model=\"GeminiChatModel\",\n freeze_url=True,\n meta={\n \"is_free_tier\": True,\n },\n)\n\nPROVIDER_OLLAMA = OllamaProvider(\n id=\"ollama\",\n name=\"Ollama\",\n is_local=True,\n require_api_key=False,\n support_model_discovery=True,\n generate_kwargs={\"max_tokens\": None},\n)\n\nPROVIDER_OPENROUTER = OpenRouterProvider(\n id=\"openrouter\",\n name=\"OpenRouter\",\n base_url=\"https://openrouter.ai/api/v1\",\n api_key_prefix=\"sk-or-v1-\",\n models=[],\n freeze_url=True,\n meta={\n \"supports_oauth\": True,\n \"is_free_tier\": True,\n },\n)\n\nGITHUB_MODELS_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"openai/gpt-4o-mini\",\n name=\"GPT-4o Mini\",\n supports_image=True,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"openai/gpt-4o\",\n name=\"GPT-4o\",\n supports_image=True,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n]\n\nPROVIDER_GITHUB_MODELS = GitHubModelsProvider(\n id=\"github-models\",\n name=\"GitHub Models\",\n base_url=\"https://models.github.ai/inference\",\n api_key_prefix=\"ghp_\",\n api_key_prefixes=[\"ghp_\", \"github_pat_\"],\n models=GITHUB_MODELS_MODELS,\n freeze_url=False,\n meta={\n \"is_free_tier\": True,\n },\n)\n\n\nPROVIDER_LMSTUDIO = LMStudioProvider(\n id=\"lmstudio\",\n name=\"LM Studio\",\n is_local=True,\n base_url=\"http://localhost:1234/v1\",\n require_api_key=False,\n api_key_prefix=\"\",\n support_model_discovery=True,\n generate_kwargs={\"max_tokens\": None},\n)\n\nPROVIDER_SILICONFLOW_CN = OpenAIProvider(\n id=\"siliconflow-cn\",\n name=\"SiliconFlow (China)\",\n base_url=\"https://api.siliconflow.cn/v1\",\n api_key_prefix=\"sk-\",\n models=[],\n freeze_url=True,\n require_api_key=True,\n provider_group=\"siliconflow\",\n provider_group_name=\"SiliconFlow\",\n provider_variant=\"china\",\n meta={\n \"is_free_tier\": True,\n },\n)\n\nPROVIDER_SILICONFLOW_INTL = OpenAIProvider(\n id=\"siliconflow-intl\",\n name=\"SiliconFlow (International)\",\n base_url=\"https://api.siliconflow.com/v1\",\n api_key_prefix=\"sk-\",\n models=[],\n freeze_url=True,\n require_api_key=True,\n provider_group=\"siliconflow\",\n provider_group_name=\"SiliconFlow\",\n provider_variant=\"international\",\n meta={\n \"is_free_tier\": True,\n },\n)\n\nPROVIDER_VOLCENGINE_CN = OpenAIProvider(\n id=\"volcengine-cn\",\n name=\"Volcano Engine\",\n base_url=\"https://ark.cn-beijing.volces.com/api/v3\",\n api_key_prefix=\"\",\n models=VOLCENGINE_MODELS,\n freeze_url=True,\n support_model_discovery=False,\n provider_group=\"volcengine\",\n provider_group_name=\"Volcano Engine\",\n provider_variant=\"open_platform\",\n)\n\nPROVIDER_VOLCENGINE_CN_CODINGPLAN = OpenAIProvider(\n id=\"volcengine-cn-codingplan\",\n name=\"Volcano Engine Coding Plan\",\n base_url=\"https://ark.cn-beijing.volces.com/api/coding/v3\",\n api_key_prefix=\"\",\n models=VOLCENGINE_CODINGPLAN_MODELS,\n support_connection_check=False,\n freeze_url=True,\n support_model_discovery=False,\n provider_group=\"volcengine\",\n provider_group_name=\"Volcano Engine\",\n provider_variant=\"coding_plan\",\n)\n\nPROVIDER_MIMO_TOKENPLAN = OpenAIProvider(\n id=\"mimo-tokenplan\",\n name=\"Xiaomi MiMo Token Plan\",\n base_url=\"https://token-plan-cn.xiaomimimo.com/v1\",\n api_key_prefix=\"\",\n models=MIMO_TOKENPLAN_MODELS,\n freeze_url=True,\n)\n\n\nclass ProviderManager: # pylint: disable=too-many-public-methods\n \"\"\"A manager class to handle all providers,\n including built-in and custom ones.\"\"\"\n\n _instance = None\n\n def __init__(self) -> None:\n # Initialize provider manager, load providers from registry and store\n # any necessary state (e.g., cached models).\n self.builtin_providers: Dict[str, Provider] = {}\n self.custom_providers: Dict[str, Provider] = {}\n self.plugin_providers: Dict[str, Dict] = {} # Plugin providers\n self.active_model: ModelSlotConfig | None = None\n self.root_path = SECRET_DIR / \"providers\"\n self.builtin_path = self.root_path / \"builtin\"\n self.custom_path = self.root_path / \"custom\"\n self.plugin_path = self.root_path / \"plugin\" # Plugin provider configs\n self._prepare_disk_storage()\n self._init_builtins()\n try:\n self._migrate_legacy_providers()\n except Exception as e:\n logger.warning(\"Failed to migrate legacy providers: %s\", e)\n self._init_from_storage()\n self._apply_default_annotations()\n\n def _prepare_disk_storage(self):\n \"\"\"Prepare directory structure\"\"\"\n for path in [\n self.root_path,\n self.builtin_path,\n self.custom_path,\n self.plugin_path,\n ]:\n path.mkdir(parents=True, exist_ok=True)\n try:\n os.chmod(path, 0o700) # Restrict permissions for security\n except Exception:\n pass\n\n def _init_builtins(self):\n self._add_builtin(PROVIDER_QWENPAW)\n self._add_builtin(PROVIDER_OLLAMA)\n self._add_builtin(PROVIDER_LMSTUDIO)\n self._add_builtin(PROVIDER_OPENROUTER)\n self._add_builtin(PROVIDER_GITHUB_MODELS)\n self._add_builtin(PROVIDER_MODELSCOPE)\n self._add_builtin(PROVIDER_DASHSCOPE)\n self._add_builtin(PROVIDER_ALIYUN_CODINGPLAN)\n self._add_builtin(PROVIDER_ALIYUN_CODINGPLAN_INTL)\n self._add_builtin(PROVIDER_ALIYUN_TOKENPLAN)\n self._add_builtin(PROVIDER_ALIYUN_TOKENPLAN_INTL)\n self._add_builtin(PROVIDER_OPENCODE)\n self._add_builtin(PROVIDER_KILO)\n self._add_builtin(PROVIDER_OPENAI)\n self._add_builtin(PROVIDER_OPENAI_RESPONSE)\n self._add_builtin(PROVIDER_AZURE_OPENAI)\n self._add_builtin(PROVIDER_ANTHROPIC)\n self._add_builtin(PROVIDER_GEMINI)\n self._add_builtin(PROVIDER_DEEPSEEK)\n self._add_builtin(PROVIDER_KIMI_CN)\n self._add_builtin(PROVIDER_KIMI_INTL)\n self._add_builtin(PROVIDER_KIMI_CODINGPLAN)\n self._add_builtin(PROVIDER_MINIMAX_CN)\n self._add_builtin(PROVIDER_MINIMAX)\n self._add_builtin(PROVIDER_ZHIPU_CN)\n self._add_builtin(PROVIDER_ZHIPU_CN_CODINGPLAN)\n self._add_builtin(PROVIDER_ZHIPU_INTL)\n self._add_builtin(PROVIDER_ZHIPU_INTL_CODINGPLAN)\n self._add_builtin(PROVIDER_SILICONFLOW_CN)\n self._add_builtin(PROVIDER_SILICONFLOW_INTL)\n self._add_builtin(PROVIDER_VOLCENGINE_CN)\n self._add_builtin(PROVIDER_VOLCENGINE_CN_CODINGPLAN)\n self._add_builtin(PROVIDER_MIMO_TOKENPLAN)\n\n def _add_builtin(self, provider: Provider):\n self.builtin_providers[provider.id] = provider\n\n async def list_provider_info(self) -> List[ProviderInfo]:\n tasks = [\n provider.get_info() for provider in self.builtin_providers.values()\n ]\n tasks += [\n provider.get_info() for provider in self.custom_providers.values()\n ]\n # Add plugin providers - directly return their ProviderInfo\n for plugin_provider in self.plugin_providers.values():\n provider_info = plugin_provider[\"info\"]\n # Plugin providers store ProviderInfo directly, no need to\n # instantiate\n tasks.append(self._get_plugin_provider_info(provider_info))\n\n provider_infos = await asyncio.gather(*tasks)\n return list(provider_infos)\n\n async def _get_plugin_provider_info(\n self,\n provider_info: ProviderInfo,\n ) -> ProviderInfo:\n \"\"\"Helper to return plugin provider info as async task.\"\"\"\n return provider_info\n\n @staticmethod\n def _normalize_provider_id(provider_id: str) -> str:\n \"\"\"Normalize provider ID for backward compatibility.\n\n Maps legacy 'copaw-local' to 'qwenpaw-local'.\n \"\"\"\n if provider_id == \"copaw-local\":\n return \"qwenpaw-local\"\n return provider_id\n\n def get_provider(self, provider_id: str) -> Provider | None:\n # Return a provider instance by its ID. This will be used to create\n # chat model instances for the agent.\n # Normalize provider ID for backward compatibility\n provider_id = self._normalize_provider_id(provider_id)\n # Check plugin providers first\n if provider_id in self.plugin_providers:\n plugin_provider = self.plugin_providers[provider_id]\n provider_info = plugin_provider[\"info\"]\n provider_class = plugin_provider[\"class\"]\n # Instantiate with **dict unpacking for Pydantic BaseModel\n return provider_class(**provider_info.model_dump())\n if provider_id in self.builtin_providers:\n return self.builtin_providers[provider_id]\n if provider_id in self.custom_providers:\n return self.custom_providers[provider_id]\n return None\n\n async def get_provider_info(self, provider_id: str) -> ProviderInfo | None:\n provider = self.get_provider(provider_id)\n return await provider.get_info() if provider else None\n\n def get_active_model(self) -> ModelSlotConfig | None:\n # Return the currently active provider/model configuration.\n return self.active_model\n\n def update_provider(self, provider_id: str, config: Dict) -> bool:\n # Update the configuration of a provider (e.g., base URL, API key).\n # This will be called when the user edits a provider's settings in the\n # UI. It should update the in-memory provider instance and persist the\n # changes to providers.json.\n # Normalize provider ID for backward compatibility\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n return False\n provider.update_config(config)\n\n # Determine save location\n is_builtin = provider_id in self.builtin_providers\n is_plugin = provider_id in self.plugin_providers\n\n if is_plugin:\n # Update plugin provider info in memory (convert Provider to\n # ProviderInfo)\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n # Save to plugin path (separate from builtin)\n self._save_plugin_provider(provider)\n else:\n self._save_provider(provider, is_builtin=is_builtin)\n\n return True\n\n def start_local_model_resume(self, local_manager) -> None:\n \"\"\"Schedule background restore of the active local model server.\"\"\"\n task = asyncio.create_task(\n self._resume_local_model(local_manager),\n name=\"qwenpaw-local-model-resume\",\n )\n task.add_done_callback(self._on_local_model_resume_done)\n\n @staticmethod\n def _on_local_model_resume_done(task: asyncio.Task[None]) -> None:\n \"\"\"Log unexpected failures from background local model restore.\"\"\"\n if task.cancelled():\n return\n\n exc = task.exception()\n if exc is not None:\n logger.warning(\n \"Background local model restore failed: %s\",\n exc,\n exc_info=exc,\n )\n logger.info(\"Background local model restore completed\")\n\n async def fetch_provider_models(\n self,\n provider_id: str,\n save: bool = True,\n ) -> List[ModelInfo]:\n \"\"\"Fetch the list of available models from a provider.\n\n Args:\n provider_id: The ID of the provider to fetch models from.\n save: If True, save the discovered models to the provider\n configuration. Defaults to True.\n\n Returns:\n List of ModelInfo objects representing available models.\n \"\"\"\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n return []\n try:\n models = await provider.fetch_models()\n if save:\n provider.extra_models = models\n # Save provider config to appropriate location\n is_plugin = provider_id in self.plugin_providers\n if is_plugin:\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n self._save_plugin_provider(provider)\n else:\n self._save_provider(\n provider,\n is_builtin=provider_id in self.builtin_providers,\n )\n return models\n except Exception as e:\n logger.warning(\n \"Failed to fetch models for provider '%s': %s\",\n provider_id,\n e,\n )\n return []\n\n def _resolve_custom_provider_id(self, provider_id: str) -> str:\n \"\"\"Resolve provider ID conflicts for a custom provider.\"\"\"\n base_id = provider_id\n if base_id in self.builtin_providers:\n base_id = f\"{base_id}-custom\"\n\n resolved_id = base_id\n while (\n resolved_id in self.builtin_providers\n or resolved_id in self.custom_providers\n ):\n resolved_id = f\"{resolved_id}-new\"\n\n return resolved_id\n\n async def add_custom_provider(self, provider_data: ProviderInfo):\n # Add a new custom provider with the given data. This will update the\n # providers.json file and make the new provider available in the UI.\n provider_payload = provider_data.model_dump()\n # ``max_input_length`` equal to the historical 128K default is only\n # distinguishable from an omitted value while the request model still\n # carries Pydantic's field-presence information. Preserve that intent\n # before model_dump/storage erase it. This is deliberately scoped to\n # the user-facing custom-provider ingestion path: legacy provider JSON\n # serialized every default field, so applying the same inference while\n # loading from disk would incorrectly mark all old 128K defaults as\n # explicit overrides.\n for field in (\"models\", \"extra_models\"):\n source_models = getattr(provider_data, field, ())\n payload_models = provider_payload.get(field, ())\n for source, payload in zip(source_models, payload_models):\n if \"max_input_length\" in source.model_fields_set:\n payload[\"max_input_length_configured\"] = True\n provider_payload[\"id\"] = self._resolve_custom_provider_id(\n provider_data.id,\n )\n provider_payload[\"is_custom\"] = True\n provider = self._provider_from_data(\n provider_payload,\n ) # Validate provider data\n # For custom providers, we assume they don't support connection check\n # without model config, to avoid false negatives in the UI.\n provider.support_connection_check = False\n self.custom_providers[provider.id] = provider\n self._save_provider(provider, is_builtin=False)\n return await provider.get_info()\n\n def remove_custom_provider(self, provider_id: str) -> bool:\n # Remove a custom provider by its ID. This will update the\n # providers.json file and remove the provider from the UI.\n if provider_id in self.custom_providers:\n del self.custom_providers[provider_id]\n provider_path = self.custom_path / f\"{provider_id}.json\"\n if provider_path.exists():\n os.remove(provider_path)\n return True\n return False\n\n async def activate_model(self, provider_id: str, model_id: str):\n # Set the active provider and model for the agent. This will update\n # providers.json and determine which provider/model is used when the\n # agent creates chat model instances.\n # Normalize provider ID for backward compatibility\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n raise ProviderError(\n message=f\"Provider '{provider_id}' not found.\",\n )\n if not provider.has_model(model_id):\n raise ModelNotFoundException(\n model_name=f\"{provider_id}/{model_id}\",\n details={\"provider_id\": provider_id, \"model_id\": model_id},\n )\n self.active_model = ModelSlotConfig(\n provider_id=provider_id,\n model=model_id,\n )\n self.save_active_model(self.active_model)\n\n self.maybe_probe_multimodal(provider_id, model_id)\n\n def maybe_probe_multimodal(self, provider_id: str, model_id: str) -> None:\n \"\"\"Schedule multimodal probing for a model if capability is unknown.\"\"\"\n provider = self.get_provider(provider_id)\n # Auto-probe multimodal if not yet probed\n for model in provider.models + provider.extra_models:\n if model.id == model_id and model.supports_multimodal is None:\n asyncio.create_task(\n self._auto_probe_multimodal(provider_id, model_id),\n )\n break\n\n async def _auto_probe_multimodal(\n self,\n provider_id: str,\n model_id: str,\n ) -> None:\n \"\"\"Background probe that doesn't block model activation.\"\"\"\n try:\n result = await self.probe_model_multimodal(provider_id, model_id)\n logger.info(\n \"Auto-probe for %s/%s: image=%s, video=%s\",\n provider_id,\n model_id,\n result.get(\"supports_image\"),\n result.get(\"supports_video\"),\n )\n # Heal a poisoned ``rejects_media`` cache entry: if the\n # probe actually saw the image, force ``rejects_media`` to\n # False so subsequent ``_reasoning`` calls stop stripping\n # media. Without this, a stale entry written from an\n # unrelated 400 (request too large, malformed block fields)\n # would silently drop every future image.\n if result.get(\"supports_image\"):\n from .model_capability_cache import get_capability_cache\n\n get_capability_cache().learn(\n f\"{provider_id}:{model_id}\",\n \"rejects_media\",\n False,\n )\n except Exception as e:\n logger.warning(\"Auto-probe multimodal failed: %s\", e)\n\n async def add_model_to_provider(\n self,\n provider_id: str,\n model_info: ModelInfo,\n ) -> ProviderInfo:\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n raise ProviderError(\n message=f\"Provider '{provider_id}' not found.\",\n )\n added, error_message = await provider.add_model(model_info)\n if not added:\n raise ProviderError(\n message=error_message,\n details={\n \"provider_id\": provider_id,\n \"model_id\": model_info.id,\n },\n )\n\n # Save provider config to appropriate location\n is_plugin = provider_id in self.plugin_providers\n if is_plugin:\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n self._save_plugin_provider(provider)\n else:\n self._save_provider(\n provider,\n is_builtin=provider_id in self.builtin_providers,\n )\n return await provider.get_info()\n\n async def update_model_config(\n self,\n provider_id: str,\n model_id: str,\n config: Dict,\n ) -> ProviderInfo:\n \"\"\"Update per-model configuration and persist to disk.\"\"\"\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n raise ProviderError(\n message=f\"Provider '{provider_id}' not found.\",\n )\n if not provider.update_model_config(model_id, config):\n raise ModelNotFoundException(\n model_name=f\"{provider_id}/{model_id}\",\n details={\"provider_id\": provider_id, \"model_id\": model_id},\n )\n\n # Save provider config to appropriate location\n is_plugin = provider_id in self.plugin_providers\n if is_plugin:\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n self._save_plugin_provider(provider)\n else:\n self._save_provider(\n provider,\n is_builtin=provider_id in self.builtin_providers,\n )\n return await provider.get_info()\n\n async def delete_model_from_provider(\n self,\n provider_id: str,\n model_id: str,\n ) -> ProviderInfo:\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n raise ProviderError(\n message=f\"Provider '{provider_id}' not found.\",\n )\n await provider.delete_model(model_id=model_id)\n\n # Save provider config to appropriate location\n is_plugin = provider_id in self.plugin_providers\n if is_plugin:\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n self._save_plugin_provider(provider)\n else:\n self._save_provider(\n provider,\n is_builtin=provider_id in self.builtin_providers,\n )\n return await provider.get_info()\n\n async def probe_model_multimodal(\n self,\n provider_id: str,\n model_id: str,\n image_only: bool = False,\n ) -> dict:\n \"\"\"Probe a model's multimodal capabilities and persist the result.\n\n Args:\n provider_id: Provider identifier.\n model_id: Model identifier.\n image_only: When True, skip the video probe for a faster result.\n Only ``supports_image`` will be accurate; ``supports_video``\n will remain at its previous value (not updated).\n \"\"\"\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n return {\"error\": f\"Provider '{provider_id}' not found\"}\n\n result = await provider.probe_model_multimodal(\n model_id,\n image_only=image_only,\n )\n\n # Update the model's capability flags.\n # For image_only probes, leave supports_video untouched so a\n # subsequent full probe can fill it in correctly.\n for model in provider.models + provider.extra_models:\n if model.id == model_id:\n model.supports_image = result.supports_image\n if not image_only:\n model.supports_video = result.supports_video\n model.supports_multimodal = result.supports_multimodal\n else:\n # Partial update: derive supports_multimodal from\n # image alone; video will be updated by the full probe.\n if result.supports_image:\n model.supports_multimodal = True\n model.probe_source = \"probed\"\n break\n\n # Compare probe result against expected baseline\n from .capability_baseline import (\n ExpectedCapabilityRegistry,\n compare_probe_result,\n )\n\n registry = ExpectedCapabilityRegistry()\n expected = registry.get_expected(provider_id, model_id)\n if expected:\n discrepancies = compare_probe_result(\n expected,\n result.supports_image,\n result.supports_video,\n )\n for d in discrepancies:\n logger.warning(\n \"Probe discrepancy: %s/%s %s expected=%s actual=%s (%s)\",\n d.provider_id,\n d.model_id,\n d.field,\n d.expected,\n d.actual,\n d.discrepancy_type,\n )\n\n # Persist to disk\n is_plugin = provider_id in self.plugin_providers\n if is_plugin:\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n self._save_plugin_provider(provider)\n else:\n self._save_provider(\n provider,\n is_builtin=provider_id in self.builtin_providers,\n )\n return {\n \"supports_image\": result.supports_image,\n \"supports_video\": result.supports_video,\n \"supports_multimodal\": result.supports_multimodal,\n \"image_message\": result.image_message,\n \"video_message\": result.video_message,\n }\n\n def _save_provider(\n self,\n provider: Provider,\n is_builtin: bool = False,\n skip_if_exists: bool = False,\n ):\n \"\"\"Save a provider configuration to disk.\n\n Sensitive fields (``api_key``) are encrypted before writing.\n \"\"\"\n provider_dir = self.builtin_path if is_builtin else self.custom_path\n provider_path = provider_dir / f\"{provider.id}.json\"\n if skip_if_exists and provider_path.exists():\n return\n data = encrypt_dict_fields(\n provider.model_dump(),\n PROVIDER_SECRET_FIELDS,\n )\n with open(provider_path, \"w\", encoding=\"utf-8\") as f:\n json.dump(data, f, ensure_ascii=False, indent=2)\n try:\n os.chmod(provider_path, 0o600)\n except OSError:\n pass\n\n def _save_plugin_provider(self, provider: Provider):\n \"\"\"Save a plugin provider configuration to disk.\n\n Sensitive fields (``api_key``) are encrypted before writing.\n \"\"\"\n provider_path = self.plugin_path / f\"{provider.id}.json\"\n data = encrypt_dict_fields(\n provider.model_dump(),\n PROVIDER_SECRET_FIELDS,\n )\n with open(provider_path, \"w\", encoding=\"utf-8\") as f:\n json.dump(data, f, ensure_ascii=False, indent=2)\n try:\n os.chmod(provider_path, 0o600)\n except OSError:\n pass\n\n def save_provider_config(\n self,\n provider_id: str,\n provider: Provider | None = None,\n ) -> None:\n \"\"\"Persist the current in-memory provider state to disk.\n\n Args:\n provider_id: The provider to save.\n provider: Optional pre-resolved provider instance. When\n supplied, this instance is saved directly — important\n for plugin providers where ``get_provider`` returns a\n fresh copy each time.\n \"\"\"\n if provider is None:\n provider = self.get_provider(provider_id)\n if provider is None:\n return\n is_plugin = provider_id in self.plugin_providers\n if is_plugin:\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n self._save_plugin_provider(provider)\n else:\n self._save_provider(\n provider,\n is_builtin=provider_id in self.builtin_providers,\n )\n\n def load_provider(\n self,\n provider_id: str,\n is_builtin: bool = False,\n ) -> Provider | None:\n \"\"\"Load a provider configuration from disk.\n\n Encrypted fields are transparently decrypted. If a legacy\n plaintext ``api_key`` is detected it is re-encrypted in place.\n \"\"\"\n provider_dir = self.builtin_path if is_builtin else self.custom_path\n provider_path = provider_dir / f\"{provider_id}.json\"\n if not provider_path.exists():\n return None\n try:\n with open(provider_path, \"r\", encoding=\"utf-8\") as f:\n data = json.load(f)\n\n needs_rewrite = self._maybe_migrate_plaintext(\n data,\n PROVIDER_SECRET_FIELDS,\n )\n data = decrypt_dict_fields(data, PROVIDER_SECRET_FIELDS)\n provider = self._provider_from_data(data)\n\n if needs_rewrite:\n try:\n self._save_provider(\n provider,\n is_builtin=is_builtin,\n skip_if_exists=False,\n )\n except Exception as enc_err:\n logger.debug(\n \"Deferred plaintext→encrypted migration\"\n \" for provider '%s': %s\",\n provider_id,\n enc_err,\n )\n\n return provider\n except Exception as e:\n logger.warning(\n \"Failed to load provider '%s' from %s: %s\",\n provider_id,\n provider_path,\n e,\n )\n return None\n\n @staticmethod\n def _maybe_migrate_plaintext(\n data: dict,\n secret_fields: frozenset[str],\n ) -> bool:\n \"\"\"Return ``True`` when *data* contains plaintext secret fields\n that should be re-encrypted on disk.\"\"\"\n for field in secret_fields:\n value = data.get(field)\n if isinstance(value, str) and value and not is_encrypted(value):\n return True\n return False\n\n # pylint: disable=too-many-return-statements\n def _provider_from_data(self, data: Dict) -> Provider:\n \"\"\"Deserialize provider data to a concrete provider type.\"\"\"\n provider_id = str(data.get(\"id\", \"\"))\n chat_model = str(data.get(\"chat_model\", \"\"))\n\n if provider_id == \"openrouter\":\n return OpenRouterProvider.model_validate(data)\n if provider_id == \"anthropic\" or chat_model == \"AnthropicChatModel\":\n return AnthropicProvider.model_validate(data)\n if provider_id == \"gemini\" or chat_model == \"GeminiChatModel\":\n return GeminiProvider.model_validate(data)\n if provider_id == \"dashscope\" or chat_model == \"DashScopeChatModel\":\n return DashScopeProvider.model_validate(data)\n if provider_id == \"ollama\":\n return OllamaProvider.model_validate(data)\n if chat_model == \"OpenAIResponseModel\":\n return OpenAIResponseProvider.model_validate(data)\n return OpenAIProvider.model_validate(data)\n\n def save_active_model(self, active_model: ModelSlotConfig):\n \"\"\"Save the active provider/model configuration to disk.\"\"\"\n active_path = self.root_path / \"active_model.json\"\n with open(active_path, \"w\", encoding=\"utf-8\") as f:\n json.dump(\n active_model.model_dump(),\n f,\n ensure_ascii=False,\n indent=2,\n )\n try:\n os.chmod(active_path, 0o600)\n except OSError:\n pass\n\n def clear_active_model(self, provider_id: str | None = None) -> bool:\n \"\"\"Clear the active provider/model configuration.\n\n If provider_id is provided, only clear when it matches the current\n active provider.\n \"\"\"\n if self.active_model is None:\n return False\n # Normalize provider ID for backward compatibility\n if provider_id is not None:\n provider_id = self._normalize_provider_id(provider_id)\n if (\n provider_id is not None\n and self.active_model.provider_id != provider_id\n ):\n return False\n\n self.active_model = None\n active_path = self.root_path / \"active_model.json\"\n try:\n active_path.unlink()\n except (FileNotFoundError, OSError):\n pass\n return True\n\n def load_active_model(self) -> ModelSlotConfig | None:\n \"\"\"Load the active provider/model configuration from disk.\"\"\"\n active_path = self.root_path / \"active_model.json\"\n if not active_path.exists():\n return None\n try:\n with open(active_path, \"r\", encoding=\"utf-8\") as f:\n data = json.load(f)\n return ModelSlotConfig.model_validate(data)\n except Exception:\n return None\n\n def _migrate_copaw_config(self) -> None:\n \"\"\"Migrate copaw-local provider config to qwenpaw-local.\"\"\"\n # 1. Migrate active model configuration (only provider_id)\n if (\n self.active_model\n and self.active_model.provider_id == \"copaw-local\"\n ):\n self.active_model.provider_id = \"qwenpaw-local\"\n self.save_active_model(self.active_model)\n logger.info(\n \"Migrated active model provider from \"\n \"'copaw-local' to 'qwenpaw-local'\",\n )\n\n # 2. Migrate stored provider config file\n copaw_config_path = self.builtin_path / \"copaw-local.json\"\n if not copaw_config_path.exists():\n return\n\n try:\n # Load old config and apply to new provider instance\n with open(copaw_config_path, \"r\", encoding=\"utf-8\") as f:\n old_config = json.load(f)\n\n # Get the new built-in provider instance\n provider = self.builtin_providers.get(\"qwenpaw-local\")\n if not provider:\n return\n\n # Apply migrated configuration (preserve extra_models as-is)\n if \"extra_models\" in old_config:\n provider.extra_models = [\n ModelInfo.model_validate(model)\n for model in old_config[\"extra_models\"]\n ]\n if \"base_url\" in old_config:\n provider.base_url = old_config[\"base_url\"]\n if \"generate_kwargs\" in old_config:\n provider.generate_kwargs = old_config[\"generate_kwargs\"]\n\n # Save using standard persistence logic (with encryption)\n self._save_provider(provider, is_builtin=True)\n\n # Remove old config file\n copaw_config_path.unlink()\n logger.info(\n \"Migrated provider config from \"\n \"'copaw-local.json' to 'qwenpaw-local.json'\",\n )\n except Exception as exc:\n logger.warning(\"Failed to migrate copaw-local config: %s\", exc)\n\n # pylint: disable=too-many-branches\n def _migrate_legacy_providers(self):\n \"\"\"Migrate from legacy providers.json format to the new structure.\"\"\"\n legacy_path = SECRET_DIR / \"providers.json\"\n if legacy_path.exists() and legacy_path.is_file():\n with open(legacy_path, \"r\", encoding=\"utf-8\") as f:\n legacy_data = json.load(f)\n builtin_providers = legacy_data.get(\"providers\", {})\n custom_providers = legacy_data.get(\"custom_providers\", {})\n active_model = legacy_data.get(\"active_llm\", {})\n # Migrate built-in providers\n for provider_id, config in builtin_providers.items():\n provider = self.get_provider(provider_id)\n if not provider:\n logger.warning(\n \"Legacy provider '%s' not found in\"\n \" registry, skipping migration for this provider.\",\n provider_id,\n )\n continue\n if \"api_key\" in config:\n provider.api_key = config[\"api_key\"]\n if \"extra_models\" in config:\n provider.extra_models = [\n ModelInfo.model_validate(model)\n for model in config[\"extra_models\"]\n ]\n if not provider.freeze_url and \"base_url\" in config:\n provider.base_url = config[\"base_url\"]\n self._save_provider(provider, is_builtin=True)\n # Migrate custom providers\n for provider_id, data in custom_providers.items():\n custom_provider = OpenAIProvider(\n id=provider_id,\n name=data.get(\"name\", provider_id),\n base_url=data.get(\"base_url\", \"\"),\n api_key=data.get(\"api_key\", \"\"),\n is_custom=True,\n )\n if \"models\" in data:\n # migrate models to extra_models field\n custom_provider.extra_models = [\n ModelInfo.model_validate(model)\n for model in data[\"models\"]\n ]\n if \"chat_model\" in data:\n custom_provider.chat_model = data[\"chat_model\"]\n self._save_provider(custom_provider, is_builtin=False)\n # Migrate active model (only provider_id, not model)\n if active_model:\n try:\n # Convert legacy copaw-local provider_id\n if active_model.get(\"provider_id\") == \"copaw-local\":\n active_model[\"provider_id\"] = \"qwenpaw-local\"\n self.active_model = ModelSlotConfig.model_validate(\n active_model,\n )\n self.save_active_model(self.active_model)\n except Exception:\n logger.warning(\n \"Failed to migrate active model, using default.\",\n )\n # Remove legacy file after migration\n try:\n os.remove(legacy_path)\n except Exception:\n logger.warning(\n \"Failed to remove legacy providers.json after migration.\",\n )\n\n @staticmethod\n def _restore_builtin_model_config(\n model: ModelInfo,\n config: dict,\n ) -> None:\n \"\"\"Restore persisted overrides onto a built-in model definition.\"\"\"\n if config[\"generate_kwargs\"]:\n model.generate_kwargs = config[\"generate_kwargs\"]\n if config[\"max_tokens\"] is not None:\n model.max_tokens = config[\"max_tokens\"]\n if config[\"max_input_length\"] is not None:\n model.max_input_length = config[\"max_input_length\"]\n configured_length = config.get(\"max_input_length\")\n model.max_input_length_configured = bool(\n config.get(\"max_input_length_configured\", False)\n or (\n configured_length is not None\n and configured_length != DEFAULT_CONTEXT_WINDOW\n ),\n )\n if config.get(\"relay_reasoning\") is not None:\n model.relay_reasoning = config[\"relay_reasoning\"]\n if config.get(\"thinking_enabled\") is not None:\n model.thinking_enabled = config[\"thinking_enabled\"]\n if config.get(\"thinking_budget\") is not None:\n model.thinking_budget = config[\"thinking_budget\"]\n if config.get(\"reasoning_effort\") is not None:\n model.reasoning_effort = config[\"reasoning_effort\"]\n if config.get(\"supports_multimodal\") is not None:\n model.supports_multimodal = config[\"supports_multimodal\"]\n if config.get(\"supports_image\") is not None:\n model.supports_image = config[\"supports_image\"]\n if config.get(\"supports_video\") is not None:\n model.supports_video = config[\"supports_video\"]\n\n def _init_from_storage(self):\n \"\"\"Initialize all providers and active model from disk storage.\"\"\"\n # Load built-in providers\n # pylint: disable=too-many-nested-blocks\n for builtin in self.builtin_providers.values():\n provider = self.load_provider(builtin.id, is_builtin=True)\n if provider:\n # inherit user-configured base_url only when freeze_url=False\n if not builtin.freeze_url:\n builtin.base_url = provider.base_url\n builtin.api_key = provider.api_key\n if provider.auth_mode != \"api_key\":\n builtin.auth_mode = provider.auth_mode\n if provider.custom_headers:\n builtin.custom_headers = provider.custom_headers\n # Restore the configurable inline-media cap for the providers\n # that support it (currently DashScope).\n if hasattr(builtin, \"max_inline_media_bytes\"):\n builtin.max_inline_media_bytes = (\n provider.max_inline_media_bytes\n )\n builtin_model_ids = {m.id for m in builtin.models}\n builtin.extra_models = [\n m\n for m in provider.extra_models\n if m.id not in builtin_model_ids\n ]\n builtin.generate_kwargs.update(provider.generate_kwargs)\n # Restore per-model config for built-in models.\n # Collect from both stored built-in models and promoted\n # extra_models (models that were user-added but are now\n # part of the built-in list).\n stored_model_config: dict = {}\n _per_model_keys = (\n \"generate_kwargs\",\n \"max_tokens\",\n \"max_input_length\",\n \"max_input_length_configured\",\n \"relay_reasoning\",\n \"thinking_enabled\",\n \"thinking_budget\",\n \"reasoning_effort\",\n \"supports_multimodal\",\n \"supports_image\",\n \"supports_video\",\n )\n for m in provider.models:\n stored_model_config[m.id] = {\n k: getattr(m, k) for k in _per_model_keys\n }\n for m in provider.extra_models:\n if m.id in builtin_model_ids:\n stored_model_config.setdefault(\n m.id,\n {k: getattr(m, k) for k in _per_model_keys},\n )\n if stored_model_config:\n for model in builtin.models:\n cfg = stored_model_config.get(model.id)\n if cfg:\n self._restore_builtin_model_config(model, cfg)\n # Load custom providers\n for provider_file in self.custom_path.glob(\"*.json\"):\n provider = self.load_provider(provider_file.stem, is_builtin=False)\n if provider:\n self.custom_providers[provider.id] = provider\n # Load active model config\n active_model = self.load_active_model()\n if active_model:\n self.active_model = active_model\n\n # Migrate copaw-local to qwenpaw-local for backwards compatibility\n self._migrate_copaw_config()\n\n def _apply_default_annotations(self):\n \"\"\"Apply doc-based default annotations for unprobed models.\n\n Models that already carry static annotations (supports_image /\n supports_video set at definition time) only need the derived\n supports_multimodal flag computed. Models with no annotations\n at all fall back to the ExpectedCapabilityRegistry.\n \"\"\"\n from .capability_baseline import ExpectedCapabilityRegistry\n\n registry = ExpectedCapabilityRegistry()\n for provider in self.builtin_providers.values():\n for model in provider.models:\n # Already fully annotated (e.g. by a prior probe) → skip\n if model.supports_multimodal is not None:\n continue\n\n # Static annotations present → compute derived flag only\n if (\n model.supports_image is not None\n or model.supports_video is not None\n ):\n model.supports_multimodal = bool(\n model.supports_image or model.supports_video,\n )\n continue\n\n # No annotations at all → fall back to registry\n expected = registry.get_expected(provider.id, model.id)\n if expected:\n model.supports_image = expected.expected_image\n model.supports_video = expected.expected_video\n model.supports_multimodal = bool(\n expected.expected_image or expected.expected_video,\n )\n model.probe_source = \"documentation\"\n\n async def _resume_local_model(self, local_manager) -> None:\n \"\"\"Resume the active local model server from the previous run.\"\"\"\n\n def _clear_local_provider():\n self.update_provider(\n \"qwenpaw-local\",\n {\n \"base_url\": \"\",\n \"extra_models\": [],\n },\n )\n\n local_models = self.get_provider(\"qwenpaw-local\").extra_models\n model_id = local_models[0].id if local_models else None\n if model_id is None:\n return\n\n installed, _ = local_manager.check_llamacpp_installation()\n if not installed:\n logger.info(\n \"Skipping local model restore because\"\n \" llama.cpp is not installed.\",\n )\n _clear_local_provider()\n return\n\n if not local_manager.is_model_downloaded(model_id):\n logger.warning(\n \"Skipping local model restore because\"\n \" model is not downloaded: %s\",\n model_id,\n )\n _clear_local_provider()\n return\n\n try:\n setup_result = await local_manager.setup_server(model_id)\n except (FileNotFoundError, RuntimeError, ValueError) as exc:\n logger.warning(\n \"Failed to restore local model server for %s: %s\",\n model_id,\n exc,\n )\n _clear_local_provider()\n return\n\n self.update_provider(\n \"qwenpaw-local\",\n {\n \"base_url\": f\"http://127.0.0.1:{setup_result.port}/v1\",\n \"extra_models\": [setup_result.model_info],\n },\n )\n\n def register_plugin_provider(\n self,\n provider_id: str,\n provider_class,\n label: str,\n base_url: str,\n metadata: Dict,\n ):\n \"\"\"Register a plugin provider.\n\n Args:\n provider_id: Provider ID\n provider_class: Provider class\n label: Display label\n base_url: API base URL\n metadata: Additional metadata\n \"\"\"\n # Get default models from provider class if available\n default_models = []\n if hasattr(provider_class, \"get_default_models\"):\n try:\n default_models = provider_class.get_default_models()\n except Exception as e:\n logger.warning(\n f\"Failed to get default models for {provider_id}: {e}\",\n )\n\n # Create ProviderInfo\n provider_info = ProviderInfo(\n id=provider_id,\n name=label,\n base_url=base_url,\n api_key=\"\", # Will be configured by user\n chat_model=metadata.get(\"chat_model\", \"OpenAIChatModel\"),\n models=default_models, # Add default models\n is_custom=False, # Mark as non-custom (like builtin, cannot be\n # deleted)\n require_api_key=metadata.get(\"require_api_key\", True),\n meta=metadata.get(\"meta\", {}), # Pass meta from plugin\n )\n\n # Check if there's a saved configuration for this plugin provider\n saved_config_path = self.plugin_path / f\"{provider_id}.json\"\n if saved_config_path.exists():\n try:\n with open(saved_config_path, \"r\", encoding=\"utf-8\") as f:\n saved_config = json.load(f)\n\n # Decrypt sensitive fields\n saved_config = decrypt_dict_fields(\n saved_config,\n PROVIDER_SECRET_FIELDS,\n )\n\n # Merge saved config (api_key, base_url, extra_models, etc)\n if \"api_key\" in saved_config:\n provider_info.api_key = saved_config[\"api_key\"]\n if \"base_url\" in saved_config:\n provider_info.base_url = saved_config[\"base_url\"]\n if \"generate_kwargs\" in saved_config:\n provider_info.generate_kwargs = saved_config[\n \"generate_kwargs\"\n ]\n # Load extra_models from saved config\n if \"extra_models\" in saved_config:\n provider_info.extra_models = [\n ModelInfo.model_validate(\n model.model_dump()\n if isinstance(model, BaseModel)\n else model,\n )\n for model in saved_config[\"extra_models\"]\n ]\n logger.info(\n f\"✓ Loaded saved config for plugin provider: \"\n f\"{provider_id} \"\n f\"({len(provider_info.extra_models)} extra model(s))\",\n )\n except Exception as e:\n logger.warning(\n f\"Failed to load saved config for {provider_id}: {e}\",\n )\n\n # Register to internal dict\n self.plugin_providers[provider_id] = {\n \"info\": provider_info,\n \"class\": provider_class,\n }\n\n logger.info(\n f\"✓ Registered plugin provider: {provider_id} \"\n f\"with {len(default_models)} default model(s)\",\n )\n\n def unregister_plugin_provider(self, provider_id: str) -> bool:\n \"\"\"Remove a plugin provider from memory.\n\n Removes the provider from ``self.plugin_providers`` so it no\n longer appears in the model list. The persisted configuration\n file (``plugin_path/{provider_id}.json``) is intentionally\n kept on disk so that user-configured keys survive a\n reinstall.\n\n Args:\n provider_id: Plugin provider identifier to remove.\n\n Returns:\n ``True`` if the provider was found and removed,\n ``False`` if it was not registered.\n \"\"\"\n if provider_id not in self.plugin_providers:\n logger.warning(\n f\"unregister_plugin_provider: '{provider_id}' not found\",\n )\n return False\n del self.plugin_providers[provider_id]\n logger.info(\n f\"Unregistered plugin provider '{provider_id}' from memory\",\n )\n return True\n\n @staticmethod\n def get_instance() -> \"ProviderManager\":\n \"\"\"Get the singleton instance of ProviderManager.\"\"\"\n if ProviderManager._instance is None:\n ProviderManager._instance = ProviderManager()\n return ProviderManager._instance\n\n @staticmethod\n def get_active_chat_model() -> ChatModelBase:\n \"\"\"Get the currently active provider/model configuration.\"\"\"\n manager = ProviderManager.get_instance()\n model = manager.get_active_model()\n if model is None or model.provider_id == \"\" or model.model == \"\":\n raise ProviderError(\n message=\"No active model configured.\",\n )\n provider = manager.get_provider(model.provider_id)\n if provider is None:\n raise ProviderError(\n message=f\"Active provider '{model.provider_id}' not found.\",\n )\n return provider.get_chat_model_instance(model.model)\n"}, "b42fdc0bec69df67b2a18537838449a98ff3add3": {"t": 1785846910.9136996, "n": 83823, "h": "ed2bd12a6def38a9", "c": "# -*- coding: utf-8 -*-\n\"\"\"A Manager class to handle all providers, including built-in and custom ones.\nIt provides a unified interface to manage providers, such as listing available\nproviders, adding/removing custom providers, and fetching provider details.\"\"\"\n\nimport asyncio\nimport json\nimport logging\nimport os\nfrom typing import Dict, List\n\nfrom agentscope.model import ChatModelBase\nfrom pydantic import BaseModel\n\nfrom qwenpaw.exceptions import ModelNotFoundException\n\nfrom ..config.config import ModelSlotConfig\nfrom ..constant import SECRET_DIR\nfrom ..exceptions import ProviderError\nfrom ..security.secret_store import (\n PROVIDER_SECRET_FIELDS,\n decrypt_dict_fields,\n encrypt_dict_fields,\n is_encrypted,\n)\nfrom .anthropic_provider import AnthropicProvider\nfrom .context_windows import DEFAULT_CONTEXT_WINDOW\nfrom .dashscope_provider import DashScopeProvider\nfrom .gemini_provider import GeminiProvider\nfrom .lmstudio_provider import LMStudioProvider\nfrom .ollama_provider import OllamaProvider\nfrom .openai_provider import (\n GitHubModelsProvider,\n KiloProvider,\n OpenAIProvider,\n OpenCodeProvider,\n)\nfrom .openai_response_provider import OpenAIResponseProvider\nfrom .openrouter_provider import OpenRouterProvider\nfrom .provider import ModelInfo, Provider, ProviderInfo\n\nlogger = logging.getLogger(__name__)\n\n# -------------------------------------------------------\n# Built-in provider definitions and their default models.\n# -------------------------------------------------------\n\nMODELSCOPE_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"Qwen/Qwen3.5-122B-A10B\",\n name=\"Qwen3.5-122B-A10B\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"ZhipuAI/GLM-5\",\n name=\"GLM-5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nDASHSCOPE_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"qwen3.7-max\",\n name=\"Qwen3.7 Max\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n thinking_enabled=True,\n relay_reasoning=False,\n ),\n ModelInfo(\n id=\"qwen3.7-plus\",\n name=\"Qwen3.7 Plus\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n thinking_enabled=True,\n relay_reasoning=False,\n ),\n ModelInfo(\n id=\"qwen3.6-plus\",\n name=\"Qwen3.6 Plus\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n thinking_enabled=True,\n relay_reasoning=False,\n ),\n ModelInfo(\n id=\"deepseek-v4-pro\",\n name=\"DeepSeek-V4 Pro\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n thinking_enabled=True,\n thinking_param_style=\"effort\",\n reasoning_effort_options=[\"high\", \"max\"],\n relay_reasoning=False,\n ),\n ModelInfo(\n id=\"glm-5.2\",\n name=\"GLM-5.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n thinking_enabled=True,\n thinking_param_style=\"effort\",\n reasoning_effort_options=[\"high\", \"max\"],\n relay_reasoning=False,\n ),\n]\n\nMIMO_TOKENPLAN_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"mimo-v2.5-pro\",\n name=\"MiMo V2.5 Pro\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"mimo-v2.5\",\n name=\"MiMo V2.5\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n]\n\nALIYUN_TOKENPLAN_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"qwen3.7-plus\",\n name=\"Qwen3.7 Plus\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3.7-max\",\n name=\"Qwen3.7 Max\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3.6-plus\",\n name=\"Qwen3.6 Plus\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3.6-flash\",\n name=\"Qwen3.6 Flash\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-v4-pro\",\n name=\"DeepSeek-V4 Pro\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-v4-flash\",\n name=\"DeepSeek-V4 Flash\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-v3.2\",\n name=\"DeepSeek-V3.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5.2\",\n name=\"GLM-5.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5.1\",\n name=\"GLM-5.1\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5\",\n name=\"GLM-5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.5\",\n name=\"MiniMax M2.5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2.6\",\n name=\"Kimi K2.6\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2.5\",\n name=\"Kimi K2.5\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n]\n\nALIYUN_CODINGPLAN_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"qwen3.6-plus\",\n name=\"Qwen3.6 Plus\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3.5-plus\",\n name=\"Qwen3.5 Plus\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5.2\",\n name=\"GLM-5.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5.1\",\n name=\"GLM-5.1\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5\",\n name=\"GLM-5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-4.7\",\n name=\"GLM-4.7\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.5\",\n name=\"MiniMax M2.5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2.5\",\n name=\"Kimi K2.5\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3-max-2026-01-23\",\n name=\"Qwen3 Max 2026-01-23\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3-coder-next\",\n name=\"Qwen3 Coder Next\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"qwen3-coder-plus\",\n name=\"Qwen3 Coder Plus\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nZHIPU_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"glm-4.7-flash\",\n name=\"GLM-4.7-Flash\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"glm-5\",\n name=\"glm-5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5.2\",\n name=\"glm-5.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5.1\",\n name=\"glm-5.1\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5-turbo\",\n name=\"glm-5-turbo\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"glm-5v-turbo\",\n name=\"glm-5v-turbo\",\n supports_image=True,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nOPENAI_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"gpt-5.2\",\n name=\"GPT-5.2\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-5\",\n name=\"GPT-5\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-5-mini\",\n name=\"GPT-5 Mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-5-nano\",\n name=\"GPT-5 Nano\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4.1\",\n name=\"GPT-4.1\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4.1-mini\",\n name=\"GPT-4.1 Mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4.1-nano\",\n name=\"GPT-4.1 Nano\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"o3\",\n name=\"o3\",\n supports_image=True,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"o4-mini\",\n name=\"o4-mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4o\",\n name=\"GPT-4o\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4o-mini\",\n name=\"GPT-4o Mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n]\n\nKILO_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"kilo-auto/free\",\n name=\"Kilo Auto (Free Router)\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"nvidia/nemotron-3-ultra-550b-a55b:free\",\n name=\"Nemotron 3 Ultra 550B\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"nvidia/nemotron-3-super-120b-a12b:free\",\n name=\"Nemotron 3 Super 120B\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"poolside/laguna-m.1:free\",\n name=\"Poolside Laguna M.1\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"poolside/laguna-xs.2:free\",\n name=\"Poolside Laguna XS.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"stepfun/step-3.7-flash:free\",\n name=\"Step 3.7 Flash\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"nex-agi/nex-n2-pro:free\",\n name=\"Nex N2 Pro\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n]\n\nOPENCODE_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"deepseek-v4-flash-free\",\n name=\"DeepSeek V4 Flash\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"mimo-v2.5-free\",\n name=\"Mimo V2.5\",\n supports_image=True,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"nemotron-3-ultra-free\",\n name=\"Nemotron 3 Ultra\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"nemotron-3-super-free\",\n name=\"Nemotron 3 Super\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n]\n\nAZURE_OPENAI_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"gpt-5-chat\",\n name=\"GPT-5 Chat\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-5-mini\",\n name=\"GPT-5 Mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-5-nano\",\n name=\"GPT-5 Nano\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4.1\",\n name=\"GPT-4.1\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4.1-mini\",\n name=\"GPT-4.1 Mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4.1-nano\",\n name=\"GPT-4.1 Nano\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4o\",\n name=\"GPT-4o\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gpt-4o-mini\",\n name=\"GPT-4o Mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n]\n\nMINIMAX_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"MiniMax-M3\",\n name=\"MiniMax M3\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.7\",\n name=\"MiniMax M2.7\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.7-highspeed\",\n name=\"MiniMax M2.7 Highspeed\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.5\",\n name=\"MiniMax M2.5 (legacy)\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.5-highspeed\",\n name=\"MiniMax M2.5 Highspeed (legacy)\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.1\",\n name=\"MiniMax M2.1 (legacy)\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2.1-highspeed\",\n name=\"MiniMax M2.1 Highspeed (legacy)\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"MiniMax-M2\",\n name=\"MiniMax M2 (legacy)\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nKIMI_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"kimi-k2.5\",\n name=\"Kimi K2.5\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2-0905-preview\",\n name=\"Kimi K2 0905 Preview\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2-0711-preview\",\n name=\"Kimi K2 0711 Preview\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2-turbo-preview\",\n name=\"Kimi K2 Turbo Preview\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2-thinking\",\n name=\"Kimi K2 Thinking\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"kimi-k2-thinking-turbo\",\n name=\"Kimi K2 Thinking Turbo\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nDEEPSEEK_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"deepseek-chat\",\n name=\"DeepSeek Chat\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-reasoner\",\n name=\"DeepSeek Reasoner\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-v4-flash\",\n name=\"DeepSeek V4 Flash\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-v4-pro\",\n name=\"DeepSeek V4 Pro\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nVOLCENGINE_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"doubao-seed-2-0-code-preview-260215\",\n name=\"Doubao-Seed-2.0-Code\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-2-0-pro-260215\",\n name=\"Doubao-Seed-2.0-pro\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-2-0-lite-260428\",\n name=\"Doubao-Seed-2.0-lite\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-code-preview-251028\",\n name=\"Doubao-Seed-Code\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"glm-4-7-251222\",\n name=\"GLM-4.7\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"deepseek-v3-2-251201\",\n name=\"DeepSeek-V3.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"doubao-seed-1-8-251228\",\n name=\"Doubao-Seed-1.8\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-2-0-mini-260428\",\n name=\"Doubao-Seed-2.0-mini\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-character-251128\",\n name=\"Doubao-Seed-Character\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nVOLCENGINE_CODINGPLAN_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"doubao-seed-2-0-code-preview-260215\",\n name=\"Doubao-Seed-2.0-Code\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-2-0-pro-260215\",\n name=\"Doubao-Seed-2.0-pro\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-2-0-lite-260428\",\n name=\"Doubao-Seed-2.0-lite\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"doubao-seed-code-preview-251028\",\n name=\"Doubao-Seed-Code\",\n supports_image=True,\n supports_video=True,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"glm-5.1\",\n name=\"GLM-5.1\",\n supports_image=False,\n supports_video=False,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"minimax-m2.7\",\n name=\"MiniMax-M2.7\",\n supports_image=False,\n supports_video=False,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"kimi-k2.6\",\n name=\"Kimi-K2.6\",\n supports_image=False,\n supports_video=False,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"kimi-k2.5\",\n name=\"Kimi-K2.5\",\n supports_image=False,\n supports_video=False,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"glm-4-7-251222\",\n name=\"GLM-4.7\",\n supports_image=False,\n supports_video=False,\n probe_source=\"probed\",\n ),\n ModelInfo(\n id=\"deepseek-v3-2-251201\",\n name=\"DeepSeek-V3.2\",\n supports_image=False,\n supports_video=False,\n probe_source=\"probed\",\n ),\n]\n\nANTHROPIC_MODELS: List[ModelInfo] = []\n\nGEMINI_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"gemini-3.1-pro-preview\",\n name=\"Gemini 3.1 Pro Preview\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gemini-3-flash-preview\",\n name=\"Gemini 3 Flash Preview\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gemini-3.1-flash-lite-preview\",\n name=\"Gemini 3.1 Flash Lite Preview\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gemini-2.5-pro\",\n name=\"Gemini 2.5 Pro\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gemini-2.5-flash\",\n name=\"Gemini 2.5 Flash\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gemini-2.5-flash-lite\",\n name=\"Gemini 2.5 Flash Lite\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n ModelInfo(\n id=\"gemini-2.0-flash\",\n name=\"Gemini 2.0 Flash\",\n supports_image=True,\n supports_video=True,\n probe_source=\"documentation\",\n ),\n]\n\nPROVIDER_MODELSCOPE = OpenAIProvider(\n id=\"modelscope\",\n name=\"ModelScope\",\n base_url=\"https://api-inference.modelscope.cn/v1\",\n api_key_prefix=\"ms\",\n models=MODELSCOPE_MODELS,\n freeze_url=True,\n)\n\nPROVIDER_DASHSCOPE = DashScopeProvider(\n id=\"dashscope\",\n name=\"DashScope\",\n base_url=\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n api_key_prefix=\"sk\",\n models=DASHSCOPE_MODELS,\n provider_group=\"aliyun\",\n provider_group_name=\"Aliyun\",\n provider_variant=\"dashscope\",\n meta={\n \"base_url_options\": [\n {\n \"label\": \"China (Beijing)\",\n \"value\": \"https://dashscope.aliyuncs.com/\"\n \"compatible-mode/v1\",\n },\n {\n \"label\": \"International (Singapore)\",\n \"value\": \"https://dashscope-intl.aliyuncs.com/\"\n \"compatible-mode/v1\",\n },\n {\n \"label\": \"US (Virginia)\",\n \"value\": \"https://dashscope-us.aliyuncs.com/\"\n \"compatible-mode/v1\",\n },\n ],\n },\n)\n\nPROVIDER_ALIYUN_CODINGPLAN = OpenAIProvider(\n id=\"aliyun-codingplan\",\n name=\"Aliyun Coding Plan (China)\",\n base_url=\"https://coding.dashscope.aliyuncs.com/v1\",\n api_key_prefix=\"sk-sp\",\n models=ALIYUN_CODINGPLAN_MODELS,\n support_connection_check=False,\n freeze_url=True,\n provider_group=\"aliyun\",\n provider_group_name=\"Aliyun\",\n provider_variant=\"coding_plan_cn\",\n)\n\nPROVIDER_ALIYUN_CODINGPLAN_INTL = OpenAIProvider(\n id=\"aliyun-codingplan-intl\",\n name=\"Aliyun Coding Plan (International)\",\n base_url=\"https://coding-intl.dashscope.aliyuncs.com/v1\",\n api_key_prefix=\"sk-sp\",\n models=ALIYUN_CODINGPLAN_MODELS,\n support_connection_check=False,\n freeze_url=True,\n provider_group=\"aliyun\",\n provider_group_name=\"Aliyun\",\n provider_variant=\"coding_plan_intl\",\n)\n\nPROVIDER_ALIYUN_TOKENPLAN = OpenAIProvider(\n id=\"aliyun-tokenplan\",\n name=\"Aliyun Token Plan\",\n base_url=(\n \"https://token-plan.cn-beijing.maas.aliyuncs.com/\" \"compatible-mode/v1\"\n ),\n api_key_prefix=\"sk-sp\",\n models=ALIYUN_TOKENPLAN_MODELS,\n support_connection_check=False,\n freeze_url=True,\n provider_group=\"aliyun\",\n provider_group_name=\"Aliyun\",\n provider_variant=\"token_plan\",\n)\n\nPROVIDER_ALIYUN_TOKENPLAN_INTL = OpenAIProvider(\n id=\"aliyun-tokenplan-intl\",\n name=\"Aliyun Token Plan (International)\",\n base_url=(\n \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/\"\n \"compatible-mode/v1\"\n ),\n api_key_prefix=\"sk-sp\",\n models=ALIYUN_TOKENPLAN_MODELS,\n support_connection_check=False,\n freeze_url=True,\n provider_group=\"aliyun\",\n provider_group_name=\"Aliyun\",\n provider_variant=\"token_plan_intl\",\n)\n\nPROVIDER_ZHIPU_CN = OpenAIProvider(\n id=\"zhipu-cn\",\n name=\"Zhipu (BigModel)\",\n base_url=\"https://open.bigmodel.cn/api/paas/v4\",\n api_key_prefix=\"\",\n models=ZHIPU_MODELS,\n freeze_url=True,\n provider_group=\"zhipu\",\n provider_group_name=\"Zhipu\",\n provider_variant=\"open_platform_cn\",\n meta={\"is_free_tier\": True},\n)\n\nPROVIDER_ZHIPU_CN_CODINGPLAN = OpenAIProvider(\n id=\"zhipu-cn-codingplan\",\n name=\"Zhipu Coding Plan (BigModel)\",\n base_url=\"https://open.bigmodel.cn/api/coding/paas/v4\",\n api_key_prefix=\"\",\n models=ZHIPU_MODELS,\n freeze_url=True,\n support_connection_check=False,\n provider_group=\"zhipu\",\n provider_group_name=\"Zhipu\",\n provider_variant=\"coding_plan_cn\",\n)\n\nPROVIDER_ZHIPU_INTL = OpenAIProvider(\n id=\"zhipu-intl\",\n name=\"Zhipu (Z.AI)\",\n base_url=\"https://api.z.ai/api/paas/v4\",\n api_key_prefix=\"\",\n models=ZHIPU_MODELS,\n freeze_url=True,\n provider_group=\"zhipu\",\n provider_group_name=\"Zhipu\",\n provider_variant=\"open_platform_intl\",\n)\n\nPROVIDER_ZHIPU_INTL_CODINGPLAN = OpenAIProvider(\n id=\"zhipu-intl-codingplan\",\n name=\"Zhipu Coding Plan (Z.AI)\",\n base_url=\"https://api.z.ai/api/coding/paas/v4\",\n api_key_prefix=\"\",\n models=ZHIPU_MODELS,\n freeze_url=True,\n support_connection_check=False,\n provider_group=\"zhipu\",\n provider_group_name=\"Zhipu\",\n provider_variant=\"coding_plan_intl\",\n)\n\nPROVIDER_QWENPAW = OpenAIProvider(\n id=\"qwenpaw-local\",\n name=\"QwenPaw Local\",\n is_local=True,\n require_api_key=False,\n)\n\nPROVIDER_OPENAI = OpenAIProvider(\n id=\"openai\",\n name=\"OpenAI\",\n base_url=\"https://api.openai.com/v1\",\n api_key_prefix=\"sk-\",\n models=OPENAI_MODELS,\n freeze_url=True,\n)\n\nPROVIDER_OPENAI_RESPONSE = OpenAIResponseProvider(\n id=\"openai-response\",\n name=\"OpenAI (Response API)\",\n base_url=\"https://api.openai.com/v1\",\n api_key_prefix=\"sk-\",\n chat_model=\"OpenAIResponseModel\",\n models=OPENAI_MODELS,\n freeze_url=True,\n)\n\nPROVIDER_OPENCODE = OpenCodeProvider(\n id=\"opencode\",\n name=\"OpenCode\",\n base_url=\"https://opencode.ai/zen/v1\",\n api_key_prefix=\"\",\n models=OPENCODE_MODELS,\n require_api_key=False,\n meta={\n \"base_url_options\": [\n {\"label\": \"OpenCode\", \"value\": \"https://opencode.ai/zen/v1\"},\n {\"label\": \"OpenCode Go\", \"value\": \"https://opencode.ai/zen/go/v1\"},\n ],\n \"is_free_tier\": True,\n },\n freeze_url=False,\n)\n\nPROVIDER_KILO = KiloProvider(\n id=\"kilo\",\n name=\"Kilo Code\",\n base_url=\"https://api.kilo.ai/api/gateway\",\n api_key_prefix=\"\",\n models=KILO_MODELS,\n require_api_key=False,\n meta={\"is_free_tier\": True},\n freeze_url=True,\n)\n\nPROVIDER_AZURE_OPENAI = OpenAIProvider(\n id=\"azure-openai\",\n name=\"Azure OpenAI\",\n api_key_prefix=\"\",\n models=AZURE_OPENAI_MODELS,\n)\n\nPROVIDER_MINIMAX = AnthropicProvider(\n id=\"minimax\",\n name=\"MiniMax (International)\",\n base_url=\"https://api.minimax.io/anthropic\",\n models=MINIMAX_MODELS,\n chat_model=\"AnthropicChatModel\",\n freeze_url=True,\n support_connection_check=False,\n provider_group=\"minimax\",\n provider_group_name=\"MiniMax\",\n provider_variant=\"open_platform_intl\",\n)\n\nPROVIDER_MINIMAX_CN = AnthropicProvider(\n id=\"minimax-cn\",\n name=\"MiniMax (China)\",\n base_url=\"https://api.minimaxi.com/anthropic\",\n models=MINIMAX_MODELS,\n chat_model=\"AnthropicChatModel\",\n freeze_url=True,\n support_connection_check=False,\n provider_group=\"minimax\",\n provider_group_name=\"MiniMax\",\n provider_variant=\"open_platform_cn\",\n)\n\nPROVIDER_KIMI_CN = OpenAIProvider(\n id=\"kimi-cn\",\n name=\"Kimi (China)\",\n base_url=\"https://api.moonshot.cn/v1\",\n api_key_prefix=\"\",\n models=KIMI_MODELS,\n freeze_url=True,\n provider_group=\"kimi\",\n provider_group_name=\"Kimi\",\n provider_variant=\"open_platform_cn\",\n)\n\nPROVIDER_KIMI_INTL = OpenAIProvider(\n id=\"kimi-intl\",\n name=\"Kimi (International)\",\n base_url=\"https://api.moonshot.ai/v1\",\n api_key_prefix=\"\",\n models=KIMI_MODELS,\n freeze_url=True,\n provider_group=\"kimi\",\n provider_group_name=\"Kimi\",\n provider_variant=\"open_platform_intl\",\n)\n\nKIMI_CODINGPLAN_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"kimi-for-coding\",\n name=\"Kimi for Coding\",\n supports_image=False,\n supports_video=False,\n probe_source=\"documentation\",\n ),\n]\n\nPROVIDER_KIMI_CODINGPLAN = OpenAIProvider(\n id=\"kimi-codingplan\",\n name=\"Kimi Coding Plan\",\n base_url=\"https://api.kimi.com/coding/v1\",\n api_key_prefix=\"sk-kimi-\",\n models=KIMI_CODINGPLAN_MODELS,\n freeze_url=True,\n support_connection_check=False,\n provider_group=\"kimi\",\n provider_group_name=\"Kimi\",\n provider_variant=\"coding_plan\",\n)\n\nPROVIDER_DEEPSEEK = OpenAIProvider(\n id=\"deepseek\",\n name=\"DeepSeek\",\n base_url=\"https://api.deepseek.com\",\n api_key_prefix=\"sk-\",\n models=DEEPSEEK_MODELS,\n freeze_url=True,\n)\n\nPROVIDER_ANTHROPIC = AnthropicProvider(\n id=\"anthropic\",\n name=\"Anthropic\",\n base_url=\"https://api.anthropic.com\",\n api_key_prefix=\"sk-ant-\",\n models=ANTHROPIC_MODELS,\n chat_model=\"AnthropicChatModel\",\n freeze_url=False,\n)\n\nPROVIDER_GEMINI = GeminiProvider(\n id=\"gemini\",\n name=\"Google Gemini\",\n base_url=\"https://generativelanguage.googleapis.com\",\n api_key_prefix=\"\",\n models=GEMINI_MODELS,\n chat_model=\"GeminiChatModel\",\n freeze_url=True,\n meta={\n \"is_free_tier\": True,\n },\n)\n\nPROVIDER_OLLAMA = OllamaProvider(\n id=\"ollama\",\n name=\"Ollama\",\n is_local=True,\n require_api_key=False,\n support_model_discovery=True,\n generate_kwargs={\"max_tokens\": None},\n)\n\nPROVIDER_OPENROUTER = OpenRouterProvider(\n id=\"openrouter\",\n name=\"OpenRouter\",\n base_url=\"https://openrouter.ai/api/v1\",\n api_key_prefix=\"sk-or-v1-\",\n models=[],\n freeze_url=True,\n meta={\n \"supports_oauth\": True,\n \"is_free_tier\": True,\n },\n)\n\nGITHUB_MODELS_MODELS: List[ModelInfo] = [\n ModelInfo(\n id=\"openai/gpt-4o-mini\",\n name=\"GPT-4o Mini\",\n supports_image=True,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n ModelInfo(\n id=\"openai/gpt-4o\",\n name=\"GPT-4o\",\n supports_image=True,\n supports_video=False,\n probe_source=\"documentation\",\n is_free=True,\n ),\n]\n\nPROVIDER_GITHUB_MODELS = GitHubModelsProvider(\n id=\"github-models\",\n name=\"GitHub Models\",\n base_url=\"https://models.github.ai/inference\",\n api_key_prefix=\"ghp_\",\n api_key_prefixes=[\"ghp_\", \"github_pat_\"],\n models=GITHUB_MODELS_MODELS,\n freeze_url=False,\n meta={\n \"is_free_tier\": True,\n },\n)\n\n\nPROVIDER_LMSTUDIO = LMStudioProvider(\n id=\"lmstudio\",\n name=\"LM Studio\",\n is_local=True,\n base_url=\"http://localhost:1234/v1\",\n require_api_key=False,\n api_key_prefix=\"\",\n support_model_discovery=True,\n generate_kwargs={\"max_tokens\": None},\n)\n\nPROVIDER_SILICONFLOW_CN = OpenAIProvider(\n id=\"siliconflow-cn\",\n name=\"SiliconFlow (China)\",\n base_url=\"https://api.siliconflow.cn/v1\",\n api_key_prefix=\"sk-\",\n models=[],\n freeze_url=True,\n require_api_key=True,\n provider_group=\"siliconflow\",\n provider_group_name=\"SiliconFlow\",\n provider_variant=\"china\",\n meta={\n \"is_free_tier\": True,\n },\n)\n\nPROVIDER_SILICONFLOW_INTL = OpenAIProvider(\n id=\"siliconflow-intl\",\n name=\"SiliconFlow (International)\",\n base_url=\"https://api.siliconflow.com/v1\",\n api_key_prefix=\"sk-\",\n models=[],\n freeze_url=True,\n require_api_key=True,\n provider_group=\"siliconflow\",\n provider_group_name=\"SiliconFlow\",\n provider_variant=\"international\",\n meta={\n \"is_free_tier\": True,\n },\n)\n\nPROVIDER_VOLCENGINE_CN = OpenAIProvider(\n id=\"volcengine-cn\",\n name=\"Volcano Engine\",\n base_url=\"https://ark.cn-beijing.volces.com/api/v3\",\n api_key_prefix=\"\",\n models=VOLCENGINE_MODELS,\n freeze_url=True,\n support_model_discovery=False,\n provider_group=\"volcengine\",\n provider_group_name=\"Volcano Engine\",\n provider_variant=\"open_platform\",\n)\n\nPROVIDER_VOLCENGINE_CN_CODINGPLAN = OpenAIProvider(\n id=\"volcengine-cn-codingplan\",\n name=\"Volcano Engine Coding Plan\",\n base_url=\"https://ark.cn-beijing.volces.com/api/coding/v3\",\n api_key_prefix=\"\",\n models=VOLCENGINE_CODINGPLAN_MODELS,\n support_connection_check=False,\n freeze_url=True,\n support_model_discovery=False,\n provider_group=\"volcengine\",\n provider_group_name=\"Volcano Engine\",\n provider_variant=\"coding_plan\",\n)\n\nPROVIDER_MIMO_TOKENPLAN = OpenAIProvider(\n id=\"mimo-tokenplan\",\n name=\"Xiaomi MiMo Token Plan\",\n base_url=\"https://token-plan-cn.xiaomimimo.com/v1\",\n api_key_prefix=\"\",\n models=MIMO_TOKENPLAN_MODELS,\n freeze_url=True,\n)\n\n\nclass ProviderManager: # pylint: disable=too-many-public-methods\n \"\"\"A manager class to handle all providers,\n including built-in and custom ones.\"\"\"\n\n _instance = None\n\n def __init__(self) -> None:\n # Initialize provider manager, load providers from registry and store\n # any necessary state (e.g., cached models).\n self.builtin_providers: Dict[str, Provider] = {}\n self.custom_providers: Dict[str, Provider] = {}\n self.plugin_providers: Dict[str, Dict] = {} # Plugin providers\n self.active_model: ModelSlotConfig | None = None\n self.root_path = SECRET_DIR / \"providers\"\n self.builtin_path = self.root_path / \"builtin\"\n self.custom_path = self.root_path / \"custom\"\n self.plugin_path = self.root_path / \"plugin\" # Plugin provider configs\n self._prepare_disk_storage()\n self._init_builtins()\n try:\n self._migrate_legacy_providers()\n except Exception as e:\n logger.warning(\"Failed to migrate legacy providers: %s\", e)\n self._init_from_storage()\n self._apply_default_annotations()\n\n def _prepare_disk_storage(self):\n \"\"\"Prepare directory structure\"\"\"\n for path in [\n self.root_path,\n self.builtin_path,\n self.custom_path,\n self.plugin_path,\n ]:\n path.mkdir(parents=True, exist_ok=True)\n try:\n os.chmod(path, 0o700) # Restrict permissions for security\n except Exception:\n pass\n\n def _init_builtins(self):\n self._add_builtin(PROVIDER_QWENPAW)\n self._add_builtin(PROVIDER_OLLAMA)\n self._add_builtin(PROVIDER_LMSTUDIO)\n self._add_builtin(PROVIDER_OPENROUTER)\n self._add_builtin(PROVIDER_GITHUB_MODELS)\n self._add_builtin(PROVIDER_MODELSCOPE)\n self._add_builtin(PROVIDER_DASHSCOPE)\n self._add_builtin(PROVIDER_ALIYUN_CODINGPLAN)\n self._add_builtin(PROVIDER_ALIYUN_CODINGPLAN_INTL)\n self._add_builtin(PROVIDER_ALIYUN_TOKENPLAN)\n self._add_builtin(PROVIDER_ALIYUN_TOKENPLAN_INTL)\n self._add_builtin(PROVIDER_OPENCODE)\n self._add_builtin(PROVIDER_KILO)\n self._add_builtin(PROVIDER_OPENAI)\n self._add_builtin(PROVIDER_OPENAI_RESPONSE)\n self._add_builtin(PROVIDER_AZURE_OPENAI)\n self._add_builtin(PROVIDER_ANTHROPIC)\n self._add_builtin(PROVIDER_GEMINI)\n self._add_builtin(PROVIDER_DEEPSEEK)\n self._add_builtin(PROVIDER_KIMI_CN)\n self._add_builtin(PROVIDER_KIMI_INTL)\n self._add_builtin(PROVIDER_KIMI_CODINGPLAN)\n self._add_builtin(PROVIDER_MINIMAX_CN)\n self._add_builtin(PROVIDER_MINIMAX)\n self._add_builtin(PROVIDER_ZHIPU_CN)\n self._add_builtin(PROVIDER_ZHIPU_CN_CODINGPLAN)\n self._add_builtin(PROVIDER_ZHIPU_INTL)\n self._add_builtin(PROVIDER_ZHIPU_INTL_CODINGPLAN)\n self._add_builtin(PROVIDER_SILICONFLOW_CN)\n self._add_builtin(PROVIDER_SILICONFLOW_INTL)\n self._add_builtin(PROVIDER_VOLCENGINE_CN)\n self._add_builtin(PROVIDER_VOLCENGINE_CN_CODINGPLAN)\n self._add_builtin(PROVIDER_MIMO_TOKENPLAN)\n\n def _add_builtin(self, provider: Provider):\n self.builtin_providers[provider.id] = provider\n\n async def list_provider_info(self) -> List[ProviderInfo]:\n tasks = [\n provider.get_info() for provider in self.builtin_providers.values()\n ]\n tasks += [\n provider.get_info() for provider in self.custom_providers.values()\n ]\n # Add plugin providers - directly return their ProviderInfo\n for plugin_provider in self.plugin_providers.values():\n provider_info = plugin_provider[\"info\"]\n # Plugin providers store ProviderInfo directly, no need to\n # instantiate\n tasks.append(self._get_plugin_provider_info(provider_info))\n\n provider_infos = await asyncio.gather(*tasks)\n return list(provider_infos)\n\n async def _get_plugin_provider_info(\n self,\n provider_info: ProviderInfo,\n ) -> ProviderInfo:\n \"\"\"Helper to return plugin provider info as async task.\"\"\"\n return provider_info\n\n @staticmethod\n def _normalize_provider_id(provider_id: str) -> str:\n \"\"\"Normalize provider ID for backward compatibility.\n\n Maps legacy 'copaw-local' to 'qwenpaw-local'.\n \"\"\"\n if provider_id == \"copaw-local\":\n return \"qwenpaw-local\"\n return provider_id\n\n def get_provider(self, provider_id: str) -> Provider | None:\n # Return a provider instance by its ID. This will be used to create\n # chat model instances for the agent.\n # Normalize provider ID for backward compatibility\n provider_id = self._normalize_provider_id(provider_id)\n # Check plugin providers first\n if provider_id in self.plugin_providers:\n plugin_provider = self.plugin_providers[provider_id]\n provider_info = plugin_provider[\"info\"]\n provider_class = plugin_provider[\"class\"]\n # Instantiate with **dict unpacking for Pydantic BaseModel\n return provider_class(**provider_info.model_dump())\n if provider_id in self.builtin_providers:\n return self.builtin_providers[provider_id]\n if provider_id in self.custom_providers:\n return self.custom_providers[provider_id]\n return None\n\n async def get_provider_info(self, provider_id: str) -> ProviderInfo | None:\n provider = self.get_provider(provider_id)\n return await provider.get_info() if provider else None\n\n def get_active_model(self) -> ModelSlotConfig | None:\n # Return the currently active provider/model configuration.\n return self.active_model\n\n def update_provider(self, provider_id: str, config: Dict) -> bool:\n # Update the configuration of a provider (e.g., base URL, API key).\n # This will be called when the user edits a provider's settings in the\n # UI. It should update the in-memory provider instance and persist the\n # changes to providers.json.\n # Normalize provider ID for backward compatibility\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n return False\n provider.update_config(config)\n\n # Determine save location\n is_builtin = provider_id in self.builtin_providers\n is_plugin = provider_id in self.plugin_providers\n\n if is_plugin:\n # Update plugin provider info in memory (convert Provider to\n # ProviderInfo)\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n # Save to plugin path (separate from builtin)\n self._save_plugin_provider(provider)\n else:\n self._save_provider(provider, is_builtin=is_builtin)\n\n return True\n\n def start_local_model_resume(self, local_manager) -> None:\n \"\"\"Schedule background restore of the active local model server.\"\"\"\n task = asyncio.create_task(\n self._resume_local_model(local_manager),\n name=\"qwenpaw-local-model-resume\",\n )\n task.add_done_callback(self._on_local_model_resume_done)\n\n @staticmethod\n def _on_local_model_resume_done(task: asyncio.Task[None]) -> None:\n \"\"\"Log unexpected failures from background local model restore.\"\"\"\n if task.cancelled():\n return\n\n exc = task.exception()\n if exc is not None:\n logger.warning(\n \"Background local model restore failed: %s\",\n exc,\n exc_info=exc,\n )\n logger.info(\"Background local model restore completed\")\n\n async def fetch_provider_models(\n self,\n provider_id: str,\n save: bool = True,\n ) -> List[ModelInfo]:\n \"\"\"Fetch the list of available models from a provider.\n\n Args:\n provider_id: The ID of the provider to fetch models from.\n save: If True, save the discovered models to the provider\n configuration. Defaults to True.\n\n Returns:\n List of ModelInfo objects representing available models.\n \"\"\"\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n return []\n try:\n models = await provider.fetch_models()\n if save:\n provider.extra_models = models\n # Save provider config to appropriate location\n is_plugin = provider_id in self.plugin_providers\n if is_plugin:\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n self._save_plugin_provider(provider)\n else:\n self._save_provider(\n provider,\n is_builtin=provider_id in self.builtin_providers,\n )\n return models\n except Exception as e:\n logger.warning(\n \"Failed to fetch models for provider '%s': %s\",\n provider_id,\n e,\n )\n return []\n\n def _resolve_custom_provider_id(self, provider_id: str) -> str:\n \"\"\"Resolve provider ID conflicts for a custom provider.\"\"\"\n base_id = provider_id\n if base_id in self.builtin_providers:\n base_id = f\"{base_id}-custom\"\n\n resolved_id = base_id\n while (\n resolved_id in self.builtin_providers\n or resolved_id in self.custom_providers\n ):\n resolved_id = f\"{resolved_id}-new\"\n\n return resolved_id\n\n async def add_custom_provider(self, provider_data: ProviderInfo):\n # Add a new custom provider with the given data. This will update the\n # providers.json file and make the new provider available in the UI.\n provider_payload = provider_data.model_dump()\n # ``max_input_length`` equal to the historical 128K default is only\n # distinguishable from an omitted value while the request model still\n # carries Pydantic's field-presence information. Preserve that intent\n # before model_dump/storage erase it. This is deliberately scoped to\n # the user-facing custom-provider ingestion path: legacy provider JSON\n # serialized every default field, so applying the same inference while\n # loading from disk would incorrectly mark all old 128K defaults as\n # explicit overrides.\n for field in (\"models\", \"extra_models\"):\n source_models = getattr(provider_data, field, ())\n payload_models = provider_payload.get(field, ())\n for source, payload in zip(source_models, payload_models):\n if \"max_input_length\" in source.model_fields_set:\n payload[\"max_input_length_configured\"] = True\n provider_payload[\"id\"] = self._resolve_custom_provider_id(\n provider_data.id,\n )\n provider_payload[\"is_custom\"] = True\n provider = self._provider_from_data(\n provider_payload,\n ) # Validate provider data\n # For custom providers, we assume they don't support connection check\n # without model config, to avoid false negatives in the UI.\n provider.support_connection_check = False\n self.custom_providers[provider.id] = provider\n self._save_provider(provider, is_builtin=False)\n return await provider.get_info()\n\n def remove_custom_provider(self, provider_id: str) -> bool:\n # Remove a custom provider by its ID. This will update the\n # providers.json file and remove the provider from the UI.\n if provider_id in self.custom_providers:\n del self.custom_providers[provider_id]\n provider_path = self.custom_path / f\"{provider_id}.json\"\n if provider_path.exists():\n os.remove(provider_path)\n return True\n return False\n\n async def activate_model(self, provider_id: str, model_id: str):\n # Set the active provider and model for the agent. This will update\n # providers.json and determine which provider/model is used when the\n # agent creates chat model instances.\n # Normalize provider ID for backward compatibility\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n raise ProviderError(\n message=f\"Provider '{provider_id}' not found.\",\n )\n if not provider.has_model(model_id):\n raise ModelNotFoundException(\n model_name=f\"{provider_id}/{model_id}\",\n details={\"provider_id\": provider_id, \"model_id\": model_id},\n )\n self.active_model = ModelSlotConfig(\n provider_id=provider_id,\n model=model_id,\n )\n self.save_active_model(self.active_model)\n\n self.maybe_probe_multimodal(provider_id, model_id)\n\n def maybe_probe_multimodal(self, provider_id: str, model_id: str) -> None:\n \"\"\"Schedule multimodal probing for a model if capability is unknown.\"\"\"\n provider = self.get_provider(provider_id)\n # Auto-probe multimodal if not yet probed\n for model in provider.models + provider.extra_models:\n if model.id == model_id and model.supports_multimodal is None:\n asyncio.create_task(\n self._auto_probe_multimodal(provider_id, model_id),\n )\n break\n\n async def _auto_probe_multimodal(\n self,\n provider_id: str,\n model_id: str,\n ) -> None:\n \"\"\"Background probe that doesn't block model activation.\"\"\"\n try:\n result = await self.probe_model_multimodal(provider_id, model_id)\n logger.info(\n \"Auto-probe for %s/%s: image=%s, video=%s\",\n provider_id,\n model_id,\n result.get(\"supports_image\"),\n result.get(\"supports_video\"),\n )\n # Heal a poisoned ``rejects_media`` cache entry: if the\n # probe actually saw the image, force ``rejects_media`` to\n # False so subsequent ``_reasoning`` calls stop stripping\n # media. Without this, a stale entry written from an\n # unrelated 400 (request too large, malformed block fields)\n # would silently drop every future image.\n if result.get(\"supports_image\"):\n from .model_capability_cache import get_capability_cache\n\n get_capability_cache().learn(\n f\"{provider_id}:{model_id}\",\n \"rejects_media\",\n False,\n )\n except Exception as e:\n logger.warning(\"Auto-probe multimodal failed: %s\", e)\n\n async def add_model_to_provider(\n self,\n provider_id: str,\n model_info: ModelInfo,\n ) -> ProviderInfo:\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n raise ProviderError(\n message=f\"Provider '{provider_id}' not found.\",\n )\n added, error_message = await provider.add_model(model_info)\n if not added:\n raise ProviderError(\n message=error_message,\n details={\n \"provider_id\": provider_id,\n \"model_id\": model_info.id,\n },\n )\n\n # Save provider config to appropriate location\n is_plugin = provider_id in self.plugin_providers\n if is_plugin:\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n self._save_plugin_provider(provider)\n else:\n self._save_provider(\n provider,\n is_builtin=provider_id in self.builtin_providers,\n )\n return await provider.get_info()\n\n async def update_model_config(\n self,\n provider_id: str,\n model_id: str,\n config: Dict,\n ) -> ProviderInfo:\n \"\"\"Update per-model configuration and persist to disk.\"\"\"\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n raise ProviderError(\n message=f\"Provider '{provider_id}' not found.\",\n )\n if not provider.update_model_config(model_id, config):\n raise ModelNotFoundException(\n model_name=f\"{provider_id}/{model_id}\",\n details={\"provider_id\": provider_id, \"model_id\": model_id},\n )\n\n # Save provider config to appropriate location\n is_plugin = provider_id in self.plugin_providers\n if is_plugin:\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n self._save_plugin_provider(provider)\n else:\n self._save_provider(\n provider,\n is_builtin=provider_id in self.builtin_providers,\n )\n return await provider.get_info()\n\n async def delete_model_from_provider(\n self,\n provider_id: str,\n model_id: str,\n ) -> ProviderInfo:\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n raise ProviderError(\n message=f\"Provider '{provider_id}' not found.\",\n )\n await provider.delete_model(model_id=model_id)\n\n # Save provider config to appropriate location\n is_plugin = provider_id in self.plugin_providers\n if is_plugin:\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n self._save_plugin_provider(provider)\n else:\n self._save_provider(\n provider,\n is_builtin=provider_id in self.builtin_providers,\n )\n return await provider.get_info()\n\n async def probe_model_multimodal(\n self,\n provider_id: str,\n model_id: str,\n image_only: bool = False,\n ) -> dict:\n \"\"\"Probe a model's multimodal capabilities and persist the result.\n\n Args:\n provider_id: Provider identifier.\n model_id: Model identifier.\n image_only: When True, skip the video probe for a faster result.\n Only ``supports_image`` will be accurate; ``supports_video``\n will remain at its previous value (not updated).\n \"\"\"\n provider_id = self._normalize_provider_id(provider_id)\n provider = self.get_provider(provider_id)\n if not provider:\n return {\"error\": f\"Provider '{provider_id}' not found\"}\n\n result = await provider.probe_model_multimodal(\n model_id,\n image_only=image_only,\n )\n\n # Update the model's capability flags.\n # For image_only probes, leave supports_video untouched so a\n # subsequent full probe can fill it in correctly.\n for model in provider.models + provider.extra_models:\n if model.id == model_id:\n model.supports_image = result.supports_image\n if not image_only:\n model.supports_video = result.supports_video\n model.supports_multimodal = result.supports_multimodal\n else:\n # Partial update: derive supports_multimodal from\n # image alone; video will be updated by the full probe.\n if result.supports_image:\n model.supports_multimodal = True\n model.probe_source = \"probed\"\n break\n\n # Compare probe result against expected baseline\n from .capability_baseline import (\n ExpectedCapabilityRegistry,\n compare_probe_result,\n )\n\n registry = ExpectedCapabilityRegistry()\n expected = registry.get_expected(provider_id, model_id)\n if expected:\n discrepancies = compare_probe_result(\n expected,\n result.supports_image,\n result.supports_video,\n )\n for d in discrepancies:\n logger.warning(\n \"Probe discrepancy: %s/%s %s expected=%s actual=%s (%s)\",\n d.provider_id,\n d.model_id,\n d.field,\n d.expected,\n d.actual,\n d.discrepancy_type,\n )\n\n # Persist to disk\n is_plugin = provider_id in self.plugin_providers\n if is_plugin:\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n self._save_plugin_provider(provider)\n else:\n self._save_provider(\n provider,\n is_builtin=provider_id in self.builtin_providers,\n )\n return {\n \"supports_image\": result.supports_image,\n \"supports_video\": result.supports_video,\n \"supports_multimodal\": result.supports_multimodal,\n \"image_message\": result.image_message,\n \"video_message\": result.video_message,\n }\n\n def _save_provider(\n self,\n provider: Provider,\n is_builtin: bool = False,\n skip_if_exists: bool = False,\n ):\n \"\"\"Save a provider configuration to disk.\n\n Sensitive fields (``api_key``) are encrypted before writing.\n \"\"\"\n provider_dir = self.builtin_path if is_builtin else self.custom_path\n provider_path = provider_dir / f\"{provider.id}.json\"\n if skip_if_exists and provider_path.exists():\n return\n data = encrypt_dict_fields(\n provider.model_dump(),\n PROVIDER_SECRET_FIELDS,\n )\n with open(provider_path, \"w\", encoding=\"utf-8\") as f:\n json.dump(data, f, ensure_ascii=False, indent=2)\n try:\n os.chmod(provider_path, 0o600)\n except OSError:\n pass\n\n def _save_plugin_provider(self, provider: Provider):\n \"\"\"Save a plugin provider configuration to disk.\n\n Sensitive fields (``api_key``) are encrypted before writing.\n \"\"\"\n provider_path = self.plugin_path / f\"{provider.id}.json\"\n data = encrypt_dict_fields(\n provider.model_dump(),\n PROVIDER_SECRET_FIELDS,\n )\n with open(provider_path, \"w\", encoding=\"utf-8\") as f:\n json.dump(data, f, ensure_ascii=False, indent=2)\n try:\n os.chmod(provider_path, 0o600)\n except OSError:\n pass\n\n def save_provider_config(\n self,\n provider_id: str,\n provider: Provider | None = None,\n ) -> None:\n \"\"\"Persist the current in-memory provider state to disk.\n\n Args:\n provider_id: The provider to save.\n provider: Optional pre-resolved provider instance. When\n supplied, this instance is saved directly — important\n for plugin providers where ``get_provider`` returns a\n fresh copy each time.\n \"\"\"\n if provider is None:\n provider = self.get_provider(provider_id)\n if provider is None:\n return\n is_plugin = provider_id in self.plugin_providers\n if is_plugin:\n provider_info = ProviderInfo(**provider.model_dump())\n self.plugin_providers[provider_id][\"info\"] = provider_info\n self._save_plugin_provider(provider)\n else:\n self._save_provider(\n provider,\n is_builtin=provider_id in self.builtin_providers,\n )\n\n def load_provider(\n self,\n provider_id: str,\n is_builtin: bool = False,\n ) -> Provider | None:\n \"\"\"Load a provider configuration from disk.\n\n Encrypted fields are transparently decrypted. If a legacy\n plaintext ``api_key`` is detected it is re-encrypted in place.\n \"\"\"\n provider_dir = self.builtin_path if is_builtin else self.custom_path\n provider_path = provider_dir / f\"{provider_id}.json\"\n if not provider_path.exists():\n return None\n try:\n with open(provider_path, \"r\", encoding=\"utf-8\") as f:\n data = json.load(f)\n\n needs_rewrite = self._maybe_migrate_plaintext(\n data,\n PROVIDER_SECRET_FIELDS,\n )\n data = decrypt_dict_fields(data, PROVIDER_SECRET_FIELDS)\n provider = self._provider_from_data(data)\n\n if needs_rewrite:\n try:\n self._save_provider(\n provider,\n is_builtin=is_builtin,\n skip_if_exists=False,\n )\n except Exception as enc_err:\n logger.debug(\n \"Deferred plaintext→encrypted migration\"\n \" for provider '%s': %s\",\n provider_id,\n enc_err,\n )\n\n return provider\n except Exception as e:\n logger.warning(\n \"Failed to load provider '%s' from %s: %s\",\n provider_id,\n provider_path,\n e,\n )\n return None\n\n @staticmethod\n def _maybe_migrate_plaintext(\n data: dict,\n secret_fields: frozenset[str],\n ) -> bool:\n \"\"\"Return ``True`` when *data* contains plaintext secret fields\n that should be re-encrypted on disk.\"\"\"\n for field in secret_fields:\n value = data.get(field)\n if isinstance(value, str) and value and not is_encrypted(value):\n return True\n return False\n\n # pylint: disable=too-many-return-statements\n def _provider_from_data(self, data: Dict) -> Provider:\n \"\"\"Deserialize provider data to a concrete provider type.\"\"\"\n provider_id = str(data.get(\"id\", \"\"))\n chat_model = str(data.get(\"chat_model\", \"\"))\n\n if provider_id == \"openrouter\":\n return OpenRouterProvider.model_validate(data)\n if provider_id == \"anthropic\" or chat_model == \"AnthropicChatModel\":\n return AnthropicProvider.model_validate(data)\n if provider_id == \"gemini\" or chat_model == \"GeminiChatModel\":\n return GeminiProvider.model_validate(data)\n if provider_id == \"dashscope\" or chat_model == \"DashScopeChatModel\":\n return DashScopeProvider.model_validate(data)\n if provider_id == \"ollama\":\n return OllamaProvider.model_validate(data)\n if chat_model == \"OpenAIResponseModel\":\n return OpenAIResponseProvider.model_validate(data)\n return OpenAIProvider.model_validate(data)\n\n def save_active_model(self, active_model: ModelSlotConfig):\n \"\"\"Save the active provider/model configuration to disk.\"\"\"\n active_path = self.root_path / \"active_model.json\"\n with open(active_path, \"w\", encoding=\"utf-8\") as f:\n json.dump(\n active_model.model_dump(),\n f,\n ensure_ascii=False,\n indent=2,\n )\n try:\n os.chmod(active_path, 0o600)\n except OSError:\n pass\n\n def clear_active_model(self, provider_id: str | None = None) -> bool:\n \"\"\"Clear the active provider/model configuration.\n\n If provider_id is provided, only clear when it matches the current\n active provider.\n \"\"\"\n if self.active_model is None:\n return False\n # Normalize provider ID for backward compatibility\n if provider_id is not None:\n provider_id = self._normalize_provider_id(provider_id)\n if (\n provider_id is not None\n and self.active_model.provider_id != provider_id\n ):\n return False\n\n self.active_model = None\n active_path = self.root_path / \"active_model.json\"\n try:\n active_path.unlink()\n except (FileNotFoundError, OSError):\n pass\n return True\n\n def load_active_model(self) -> ModelSlotConfig | None:\n \"\"\"Load the active provider/model configuration from disk.\"\"\"\n active_path = self.root_path / \"active_model.json\"\n if not active_path.exists():\n return None\n try:\n with open(active_path, \"r\", encoding=\"utf-8\") as f:\n data = json.load(f)\n return ModelSlotConfig.model_validate(data)\n except Exception:\n return None\n\n def _migrate_copaw_config(self) -> None:\n \"\"\"Migrate copaw-local provider config to qwenpaw-local.\"\"\"\n # 1. Migrate active model configuration (only provider_id)\n if (\n self.active_model\n and self.active_model.provider_id == \"copaw-local\"\n ):\n self.active_model.provider_id = \"qwenpaw-local\"\n self.save_active_model(self.active_model)\n logger.info(\n \"Migrated active model provider from \"\n \"'copaw-local' to 'qwenpaw-local'\",\n )\n\n # 2. Migrate stored provider config file\n copaw_config_path = self.builtin_path / \"copaw-local.json\"\n if not copaw_config_path.exists():\n return\n\n try:\n # Load old config and apply to new provider instance\n with open(copaw_config_path, \"r\", encoding=\"utf-8\") as f:\n old_config = json.load(f)\n\n # Get the new built-in provider instance\n provider = self.builtin_providers.get(\"qwenpaw-local\")\n if not provider:\n return\n\n # Apply migrated configuration (preserve extra_models as-is)\n if \"extra_models\" in old_config:\n provider.extra_models = [\n ModelInfo.model_validate(model)\n for model in old_config[\"extra_models\"]\n ]\n if \"base_url\" in old_config:\n provider.base_url = old_config[\"base_url\"]\n if \"generate_kwargs\" in old_config:\n provider.generate_kwargs = old_config[\"generate_kwargs\"]\n\n # Save using standard persistence logic (with encryption)\n self._save_provider(provider, is_builtin=True)\n\n # Remove old config file\n copaw_config_path.unlink()\n logger.info(\n \"Migrated provider config from \"\n \"'copaw-local.json' to 'qwenpaw-local.json'\",\n )\n except Exception as exc:\n logger.warning(\"Failed to migrate copaw-local config: %s\", exc)\n\n # pylint: disable=too-many-branches\n def _migrate_legacy_providers(self):\n \"\"\"Migrate from legacy providers.json format to the new structure.\"\"\"\n legacy_path = SECRET_DIR / \"providers.json\"\n if legacy_path.exists() and legacy_path.is_file():\n with open(legacy_path, \"r\", encoding=\"utf-8\") as f:\n legacy_data = json.load(f)\n builtin_providers = legacy_data.get(\"providers\", {})\n custom_providers = legacy_data.get(\"custom_providers\", {})\n active_model = legacy_data.get(\"active_llm\", {})\n # Migrate built-in providers\n for provider_id, config in builtin_providers.items():\n provider = self.get_provider(provider_id)\n if not provider:\n logger.warning(\n \"Legacy provider '%s' not found in\"\n \" registry, skipping migration for this provider.\",\n provider_id,\n )\n continue\n if \"api_key\" in config:\n provider.api_key = config[\"api_key\"]\n if \"extra_models\" in config:\n provider.extra_models = [\n ModelInfo.model_validate(model)\n for model in config[\"extra_models\"]\n ]\n if not provider.freeze_url and \"base_url\" in config:\n provider.base_url = config[\"base_url\"]\n self._save_provider(provider, is_builtin=True)\n # Migrate custom providers\n for provider_id, data in custom_providers.items():\n custom_provider = OpenAIProvider(\n id=provider_id,\n name=data.get(\"name\", provider_id),\n base_url=data.get(\"base_url\", \"\"),\n api_key=data.get(\"api_key\", \"\"),\n is_custom=True,\n )\n if \"models\" in data:\n # migrate models to extra_models field\n custom_provider.extra_models = [\n ModelInfo.model_validate(model)\n for model in data[\"models\"]\n ]\n if \"chat_model\" in data:\n custom_provider.chat_model = data[\"chat_model\"]\n self._save_provider(custom_provider, is_builtin=False)\n # Migrate active model (only provider_id, not model)\n if active_model:\n try:\n # Convert legacy copaw-local provider_id\n if active_model.get(\"provider_id\") == \"copaw-local\":\n active_model[\"provider_id\"] = \"qwenpaw-local\"\n self.active_model = ModelSlotConfig.model_validate(\n active_model,\n )\n self.save_active_model(self.active_model)\n except Exception:\n logger.warning(\n \"Failed to migrate active model, using default.\",\n )\n # Remove legacy file after migration\n try:\n os.remove(legacy_path)\n except Exception:\n logger.warning(\n \"Failed to remove legacy providers.json after migration.\",\n )\n\n @staticmethod\n def _restore_builtin_model_config(\n model: ModelInfo,\n config: dict,\n ) -> None:\n \"\"\"Restore persisted overrides onto a built-in model definition.\"\"\"\n if config[\"generate_kwargs\"]:\n model.generate_kwargs = config[\"generate_kwargs\"]\n if config[\"max_tokens\"] is not None:\n model.max_tokens = config[\"max_tokens\"]\n if config[\"max_input_length\"] is not None:\n model.max_input_length = config[\"max_input_length\"]\n configured_length = config.get(\"max_input_length\")\n model.max_input_length_configured = bool(\n config.get(\"max_input_length_configured\", False)\n or (\n configured_length is not None\n and configured_length != DEFAULT_CONTEXT_WINDOW\n ),\n )\n if config.get(\"relay_reasoning\") is not None:\n model.relay_reasoning = config[\"relay_reasoning\"]\n if config.get(\"thinking_enabled\") is not None:\n model.thinking_enabled = config[\"thinking_enabled\"]\n if config.get(\"thinking_budget\") is not None:\n model.thinking_budget = config[\"thinking_budget\"]\n if config.get(\"reasoning_effort\") is not None:\n model.reasoning_effort = config[\"reasoning_effort\"]\n if config.get(\"supports_multimodal\") is not None:\n model.supports_multimodal = config[\"supports_multimodal\"]\n if config.get(\"supports_image\") is not None:\n model.supports_image = config[\"supports_image\"]\n if config.get(\"supports_video\") is not None:\n model.supports_video = config[\"supports_video\"]\n\n def _init_from_storage(self):\n \"\"\"Initialize all providers and active model from disk storage.\"\"\"\n # Load built-in providers\n # pylint: disable=too-many-nested-blocks\n for builtin in self.builtin_providers.values():\n provider = self.load_provider(builtin.id, is_builtin=True)\n if provider:\n # inherit user-configured base_url only when freeze_url=False\n if not builtin.freeze_url:\n builtin.base_url = provider.base_url\n builtin.api_key = provider.api_key\n if provider.auth_mode != \"api_key\":\n builtin.auth_mode = provider.auth_mode\n if provider.custom_headers:\n builtin.custom_headers = provider.custom_headers\n # Restore the configurable inline-media cap for the providers\n # that support it (currently DashScope).\n if hasattr(builtin, \"max_inline_media_bytes\"):\n builtin.max_inline_media_bytes = (\n provider.max_inline_media_bytes\n )\n builtin_model_ids = {m.id for m in builtin.models}\n builtin.extra_models = [\n m\n for m in provider.extra_models\n if m.id not in builtin_model_ids\n ]\n builtin.generate_kwargs.update(provider.generate_kwargs)\n # Restore per-model config for built-in models.\n # Collect from both stored built-in models and promoted\n # extra_models (models that were user-added but are now\n # part of the built-in list).\n stored_model_config: dict = {}\n _per_model_keys = (\n \"generate_kwargs\",\n \"max_tokens\",\n \"max_input_length\",\n \"max_input_length_configured\",\n \"relay_reasoning\",\n \"thinking_enabled\",\n \"thinking_budget\",\n \"reasoning_effort\",\n \"supports_multimodal\",\n \"supports_image\",\n \"supports_video\",\n )\n for m in provider.models:\n stored_model_config[m.id] = {\n k: getattr(m, k) for k in _per_model_keys\n }\n for m in provider.extra_models:\n if m.id in builtin_model_ids:\n stored_model_config.setdefault(\n m.id,\n {k: getattr(m, k) for k in _per_model_keys},\n )\n if stored_model_config:\n for model in builtin.models:\n cfg = stored_model_config.get(model.id)\n if cfg:\n self._restore_builtin_model_config(model, cfg)\n # Load custom providers\n for provider_file in self.custom_path.glob(\"*.json\"):\n provider = self.load_provider(provider_file.stem, is_builtin=False)\n if provider:\n self.custom_providers[provider.id] = provider\n # Load active model config\n active_model = self.load_active_model()\n if active_model:\n self.active_model = active_model\n\n # Migrate copaw-local to qwenpaw-local for backwards compatibility\n self._migrate_copaw_config()\n\n def _apply_default_annotations(self):\n \"\"\"Apply doc-based default annotations for unprobed models.\n\n Models that already carry static annotations (supports_image /\n supports_video set at definition time) only need the derived\n supports_multimodal flag computed. Models with no annotations\n at all fall back to the ExpectedCapabilityRegistry.\n \"\"\"\n from .capability_baseline import ExpectedCapabilityRegistry\n\n registry = ExpectedCapabilityRegistry()\n for provider in self.builtin_providers.values():\n for model in provider.models:\n # Already fully annotated (e.g. by a prior probe) → skip\n if model.supports_multimodal is not None:\n continue\n\n # Static annotations present → compute derived flag only\n if (\n model.supports_image is not None\n or model.supports_video is not None\n ):\n model.supports_multimodal = bool(\n model.supports_image or model.supports_video,\n )\n continue\n\n # No annotations at all → fall back to registry\n expected = registry.get_expected(provider.id, model.id)\n if expected:\n model.supports_image = expected.expected_image\n model.supports_video = expected.expected_video\n model.supports_multimodal = bool(\n expected.expected_image or expected.expected_video,\n )\n model.probe_source = \"documentation\"\n\n async def _resume_local_model(self, local_manager) -> None:\n \"\"\"Resume the active local model server from the previous run.\"\"\"\n\n def _clear_local_provider():\n self.update_provider(\n \"qwenpaw-local\",\n {\n \"base_url\": \"\",\n \"extra_models\": [],\n },\n )\n\n local_models = self.get_provider(\"qwenpaw-local\").extra_models\n model_id = local_models[0].id if local_models else None\n if model_id is None:\n return\n\n installed, _ = local_manager.check_llamacpp_installation()\n if not installed:\n logger.info(\n \"Skipping local model restore because\"\n \" llama.cpp is not installed.\",\n )\n _clear_local_provider()\n return\n\n if not local_manager.is_model_downloaded(model_id):\n logger.warning(\n \"Skipping local model restore because\"\n \" model is not downloaded: %s\",\n model_id,\n )\n _clear_local_provider()\n return\n\n try:\n setup_result = await local_manager.setup_server(model_id)\n except (FileNotFoundError, RuntimeError, ValueError) as exc:\n logger.warning(\n \"Failed to restore local model server for %s: %s\",\n model_id,\n exc,\n )\n _clear_local_provider()\n return\n\n self.update_provider(\n \"qwenpaw-local\",\n {\n \"base_url\": f\"http://127.0.0.1:{setup_result.port}/v1\",\n \"extra_models\": [setup_result.model_info],\n },\n )\n\n def register_plugin_provider(\n self,\n provider_id: str,\n provider_class,\n label: str,\n base_url: str,\n metadata: Dict,\n ):\n \"\"\"Register a plugin provider.\n\n Args:\n provider_id: Provider ID\n provider_class: Provider class\n label: Display label\n base_url: API base URL\n metadata: Additional metadata\n \"\"\"\n # Get default models from provider class if available\n default_models = []\n if hasattr(provider_class, \"get_default_models\"):\n try:\n default_models = provider_class.get_default_models()\n except Exception as e:\n logger.warning(\n f\"Failed to get default models for {provider_id}: {e}\",\n )\n\n # Create ProviderInfo\n provider_info = ProviderInfo(\n id=provider_id,\n name=label,\n base_url=base_url,\n api_key=\"\", # Will be configured by user\n chat_model=metadata.get(\"chat_model\", \"OpenAIChatModel\"),\n models=default_models, # Add default models\n is_custom=False, # Mark as non-custom (like builtin, cannot be\n # deleted)\n require_api_key=metadata.get(\"require_api_key\", True),\n meta=metadata.get(\"meta\", {}), # Pass meta from plugin\n )\n\n # Check if there's a saved configuration for this plugin provider\n saved_config_path = self.plugin_path / f\"{provider_id}.json\"\n if saved_config_path.exists():\n try:\n with open(saved_config_path, \"r\", encoding=\"utf-8\") as f:\n saved_config = json.load(f)\n\n # Decrypt sensitive fields\n saved_config = decrypt_dict_fields(\n saved_config,\n PROVIDER_SECRET_FIELDS,\n )\n\n # Merge saved config (api_key, base_url, extra_models, etc)\n if \"api_key\" in saved_config:\n provider_info.api_key = saved_config[\"api_key\"]\n if \"base_url\" in saved_config:\n provider_info.base_url = saved_config[\"base_url\"]\n if \"generate_kwargs\" in saved_config:\n provider_info.generate_kwargs = saved_config[\n \"generate_kwargs\"\n ]\n # Load extra_models from saved config\n if \"extra_models\" in saved_config:\n provider_info.extra_models = [\n ModelInfo.model_validate(\n model.model_dump()\n if isinstance(model, BaseModel)\n else model,\n )\n for model in saved_config[\"extra_models\"]\n ]\n logger.info(\n f\"✓ Loaded saved config for plugin provider: \"\n f\"{provider_id} \"\n f\"({len(provider_info.extra_models)} extra model(s))\",\n )\n except Exception as e:\n logger.warning(\n f\"Failed to load saved config for {provider_id}: {e}\",\n )\n\n # Register to internal dict\n self.plugin_providers[provider_id] = {\n \"info\": provider_info,\n \"class\": provider_class,\n }\n\n logger.info(\n f\"✓ Registered plugin provider: {provider_id} \"\n f\"with {len(default_models)} default model(s)\",\n )\n\n def unregister_plugin_provider(self, provider_id: str) -> bool:\n \"\"\"Remove a plugin provider from memory.\n\n Removes the provider from ``self.plugin_providers`` so it no\n longer appears in the model list. The persisted configuration\n file (``plugin_path/{provider_id}.json``) is intentionally\n kept on disk so that user-configured keys survive a\n reinstall.\n\n Args:\n provider_id: Plugin provider identifier to remove.\n\n Returns:\n ``True`` if the provider was found and removed,\n ``False`` if it was not registered.\n \"\"\"\n if provider_id not in self.plugin_providers:\n logger.warning(\n f\"unregister_plugin_provider: '{provider_id}' not found\",\n )\n return False\n del self.plugin_providers[provider_id]\n logger.info(\n f\"Unregistered plugin provider '{provider_id}' from memory\",\n )\n return True\n\n @staticmethod\n def get_instance() -> \"ProviderManager\":\n \"\"\"Get the singleton instance of ProviderManager.\"\"\"\n if ProviderManager._instance is None:\n ProviderManager._instance = ProviderManager()\n return ProviderManager._instance\n\n @staticmethod\n def get_active_chat_model() -> ChatModelBase:\n \"\"\"Get the currently active provider/model configuration.\"\"\"\n manager = ProviderManager.get_instance()\n model = manager.get_active_model()\n if model is None or model.provider_id == \"\" or model.model == \"\":\n raise ProviderError(\n message=\"No active model configured.\",\n )\n provider = manager.get_provider(model.provider_id)\n if provider is None:\n raise ProviderError(\n message=f\"Active provider '{model.provider_id}' not found.\",\n )\n return provider.get_chat_model_instance(model.model)\n"}}, "website/public/docs/config.en.md": {"e524dfe78d4a69b2441d8f611935bbe0b28cb7da": {"t": 1785674141.283437, "n": 48521, "h": "958a9ab284dac0f8", "c": "# Config & Working Directory\n\nThis page covers:\n\n- **Directory structure** — Where files are stored and the purpose of each directory\n- **Environment variables** — How to customize paths and behavior\n- **Configuration files** — Complete field description for `config.json` and `agent.json`\n\nFrom **v0.1.0**, QwenPaw supports **multi-agent**. Configuration is split into two layers:\n\n1. **Global config** (`config.json`) — Model providers, agent list, global settings\n2. **Agent config** (`agent.json`) — Independent config for each agent (channels, heartbeat, tools, etc.)\n\n---\n\n## Directory Structure\n\nThe default working directory is `~/.qwenpaw`. After running `qwenpaw init`, the complete structure looks like:\n\n```\n$QWENPAW_WORKING_DIR/ # Default ~/.qwenpaw\n├── config.json # Global config\n├── workspaces/\n│ ├── default/ # Default agent workspace\n│ │ ├── agent.json # Agent config\n│ │ ├── chats.json # Conversation history\n│ │ ├── jobs.json # Cron jobs\n│ │ ├── token_usage.json # Token usage records\n│ │ ├── AGENTS.md # Persona file\n│ │ ├── SOUL.md # Persona file\n│ │ ├── PROFILE.md # Persona file\n│ │ ├── BOOTSTRAP.md # Initial setup guide (auto-deleted after completion)\n│ │ ├── MEMORY.md # Long-term memory\n│ │ ├── skills/ # Workspace-local skills\n│ │ ├── skill.json # Skill enabled state and config\n│ │ ├── memory/ # Daily memory files\n│ │ └── browser/ # Browser user data (cookies, cache, etc.)\n│ └── abc123/ # Other agent workspace\n│ └── ...\n└── skill_pool/ # Local shared skill pool\n ├── skill.json # Pool metadata\n └── ...\n\n$QWENPAW_SECRET_DIR/ # Default ~/.qwenpaw.secret\n├── providers.json # Model provider config and API keys\n└── envs.json # Environment variables\n```\n\n> **Path explanation:** `$QWENPAW_WORKING_DIR` and `$QWENPAW_SECRET_DIR` are environment variables, with default values of `~/.qwenpaw` and `~/.qwenpaw.secret` respectively. They can be customized via environment variables, see \"Environment Variables\" section below.\n\n### Directory Explanation\n\n**Global Directory (`~/.qwenpaw/`)**\n\n| File / Directory | Purpose |\n| ---------------- | ----------------------------------------------------- |\n| `config.json` | Global config (model providers, env vars, agent list) |\n| `workspaces/` | All agent workspace directories |\n\n**Agent Workspace (`~/.qwenpaw/workspaces/{agent_id}/`)**\n\n| File / Directory | Purpose |\n| ------------------ | ------------------------------------------------------------ |\n| `agent.json` | Agent config (channels, heartbeat, tools, skills, MCP, etc.) |\n| `chats.json` | Conversation history |\n| `jobs.json` | Cron job list |\n| `token_usage.json` | Token usage records |\n| `AGENTS.md` | Persona file (see [Agent Persona](./persona)) |\n| `SOUL.md` | Persona file (see [Agent Persona](./persona)) |\n| `PROFILE.md` | Persona file (see [Agent Persona](./persona)) |\n| `BOOTSTRAP.md` | Initial setup guide (auto-deleted after completion) |\n| `MEMORY.md` | Long-term memory (see [Memory](./memory)) |\n| `skills/` | Skills available in this workspace |\n| `skill.json` | Skill enabled state, channel routing, and config |\n| `memory/` | Daily memory files (see [Memory](./memory)) |\n| `browser/` | Browser user data (cookies, cache, localStorage, etc.) |\n\n> **Persona files:** Agent behavior and personality are defined by persona files. Running `qwenpaw init` automatically creates template files based on your chosen language (`zh` / `en` / `ru`). For detailed explanation and management, see [Agent Persona](./persona).\n\n> **Multi-Agent:** See the [Multi-Agent](./multi-agent) documentation for details.\n\n---\n\n## Environment Variables\n\nYou can customize paths and behavior via environment variables:\n\n**Path-related:**\n\n| Variable | Default | Description |\n| -------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `QWENPAW_WORKING_DIR` | `~/.qwenpaw` | Working directory root path |\n| `QWENPAW_SECRET_DIR` | `~/.qwenpaw.secret` | Sensitive data directory (stores `providers.json` and `envs.json`). Docker default is `/app/working.secret` |\n| `QWENPAW_KEYRING_ACCOUNT` | _(auto)_ | OS keychain account name for the master key. Defaults to `master_key`; when `QWENPAW_WORKING_DIR`/`QWENPAW_SECRET_DIR` are set (e.g. a dev checkout) it auto-derives a per-install account so a dev install never overwrites the stable install's key. Set explicitly to name a profile. |\n| `QWENPAW_CONFIG_FILE` | `config.json` | Config file name (relative to `QWENPAW_WORKING_DIR`) |\n| `QWENPAW_HEARTBEAT_FILE` | `HEARTBEAT.md` | Heartbeat file name (relative to agent workspace) |\n| `QWENPAW_JOBS_FILE` | `jobs.json` | Cron jobs file name (relative to agent workspace) |\n| `QWENPAW_CHATS_FILE` | `chats.json` | Conversation history file name (relative to agent workspace) |\n| `QWENPAW_TOKEN_USAGE_FILE` | `token_usage.json` | Token usage record file name (relative to agent workspace) |\n\n**Other configuration:**\n\n| Variable | Default | Description |\n| ------------------------------------ | --------------- | ---------------------------------------------------------------------------- |\n| `QWENPAW_LOG_LEVEL` | `info` | Log level (`debug` / `info` / `warning` / `error` / `critical`) |\n| `QWENPAW_LOG_MAX_SIZE` | `5MiB` | Maximum active log size; accepts bytes or suffixes such as `10MB` and `1GiB` |\n| `QWENPAW_LOG_MAX_BACKUPS` | `3` | Number of rotated log backups to retain; `0` disables backups |\n| `QWENPAW_MEMORY_COMPACT_THRESHOLD` | `100000` | Character threshold to trigger memory compaction |\n| `QWENPAW_MEMORY_COMPACT_KEEP_RECENT` | `3` | Number of recent messages to keep after compaction |\n| `QWENPAW_MEMORY_COMPACT_RATIO` | `0.7` | Threshold ratio for triggering compaction (relative to context window size) |\n| `QWENPAW_CONSOLE_STATIC_DIR` | _(auto-detect)_ | Console frontend static files path |\n\n**Security & Authentication:**\n\n| Variable | Default | Description |\n| ---------------------------- | ------- | -------------------------------------------------- |\n| `QWENPAW_AUTH_ENABLED` | `false` | Whether to enable Web console login authentication |\n| `QWENPAW_AUTH_USERNAME` | - | Admin username for auto-registration (optional) |\n| `QWENPAW_AUTH_PASSWORD` | - | Admin password for auto-registration (optional) |\n| `QWENPAW_TOOL_GUARD_ENABLED` | `true` | Whether to enable tool guard |\n| `QWENPAW_SKILL_SCAN_MODE` | `warn` | Skill scanning mode (`block` / `warn` / `off`) |\n\nExample — use a different working dir for this shell:\n\n```bash\nexport QWENPAW_WORKING_DIR=/home/me/my_qwenpaw\nqwenpaw app\n```\n\nConfig, HEARTBEAT, jobs, memory, etc. will be read/written under\n`/home/me/my_qwenpaw`.\n\n---\n\n## Configuration File Structure\n\nStarting from **v0.1.0**, configuration is split into two layers:\n\n1. **Global config** - `~/.qwenpaw/config.json` (providers, environment variables, agent list)\n2. **Agent config** - `~/.qwenpaw/workspaces/{agent_id}/agent.json` (per-agent settings)\n\n### Global config.json\n\nStores globally shared configuration:\n\n```json\n{\n \"agents\": {\n \"active_agent\": \"default\",\n \"profiles\": {\n \"default\": {\n \"id\": \"default\",\n \"name\": \"Default Agent\",\n \"description\": \"Default workspace agent\",\n \"enabled\": true\n },\n \"abc123\": {\n \"id\": \"abc123\",\n \"name\": \"Code Assistant\",\n \"description\": \"Focuses on code review and development\",\n \"enabled\": true\n }\n }\n },\n \"last_api\": {\n \"host\": \"127.0.0.1\",\n \"port\": 8088\n },\n \"show_tool_details\": true\n}\n```\n\n**Global config.json field descriptions:**\n\n| Field | Type | Default | Description |\n| --------------------- | -------------- | ------------------- | ----------------------------------------------------------------- |\n| `agents.active_agent` | string | `\"default\"` | Currently active agent ID |\n| `agents.profiles` | object | `{}` | Agent profile references (key is agent_id) |\n| `last_api.host` | string \\| null | `null` | Host address from last `qwenpaw app` start |\n| `last_api.port` | int \\| null | `null` | Port from last `qwenpaw app` start |\n| `show_tool_details` | bool | `true` | Whether to show tool call/return details in channel messages |\n| `user_timezone` | string | _(system timezone)_ | IANA timezone name (e.g., `\"Asia/Shanghai\"`) |\n| `last_dispatch` | object \\| null | `null` | Last message dispatch target (used for heartbeat `target=\"last\"`) |\n\n**`agents.profiles[agent_id]` reference fields:**\n\n| Field | Type | Required | Description |\n| --------------- | ------ | -------- | ----------------------------------------------------------------------------- |\n| `id` | string | Yes | Agent unique identifier |\n| `name` | string | Yes | Agent display name |\n| `description` | string | No | Agent description (used for multi-agent collaboration) |\n| `enabled` | bool | Yes | Whether to enable this agent |\n| `workspace_dir` | string | No | Workspace path (optional, defaults to `$QWENPAW_WORKING_DIR/workspaces/{id}`) |\n\n> **Backward compatibility:** The global config.json still supports `channels`, `mcp`, `tools`, `security` and other fields for backward compatibility with older versions. In multi-agent mode, these configurations should be set in each agent's `agent.json`.\n>\n> **Configuration priority:** The agent's `agent.json` takes precedence over the global `config.json`. When the same field is configured in both places, the system uses the value from `agent.json`. For multi-agent mode, it's recommended to put all configurations in each agent's `agent.json`.\n\n> **Model provider configuration** is stored in `$QWENPAW_SECRET_DIR/providers.json` (default `~/.qwenpaw.secret/providers.json`).\n> **Environment variables** are stored in `$QWENPAW_SECRET_DIR/envs.json` (default `~/.qwenpaw.secret/envs.json`).\n\n### Agent config (agent.json)\n\nEach agent has an independent `agent.json` in its workspace directory (`~/.qwenpaw/workspaces/{agent_id}/`) that stores all of its configuration (channels, tools, heartbeat, MCP, security, etc.). This allows different agents to have completely different configurations without interfering with each other.\n\n```json\n{\n \"id\": \"default\",\n \"name\": \"Default Agent\",\n \"description\": \"Default workspace agent\",\n \"workspace_dir\": \"\",\n \"channels\": {\n \"console\": {\n \"enabled\": true,\n \"bot_prefix\": \"\"\n },\n \"dingtalk\": {\n \"enabled\": false,\n \"bot_prefix\": \"\",\n \"client_id\": \"\",\n \"client_secret\": \"\"\n }\n },\n \"mcp\": {\n \"clients\": {\n \"filesystem\": {\n \"name\": \"Filesystem Access\",\n \"enabled\": true,\n \"command\": \"npx\",\n \"args\": [\n \"-y\",\n \"@modelcontextprotocol/server-filesystem\",\n \"/path/to/folder\"\n ]\n }\n }\n },\n \"heartbeat\": {\n \"enabled\": false,\n \"every\": \"30m\",\n \"target\": \"main\",\n \"timeoutSeconds\": 300,\n \"activeHours\": null\n },\n \"running\": {\n \"max_iters\": 50,\n \"llm_retry_enabled\": true,\n \"llm_max_retries\": 3,\n \"llm_backoff_base\": 1.0,\n \"llm_backoff_cap\": 10.0,\n \"max_input_length\": 131072\n },\n \"active_model\": null,\n \"language\": \"en\",\n \"system_prompt_files\": [\"AGENTS.md\", \"SOUL.md\", \"PROFILE.md\"],\n \"tools\": {\n \"builtin_tools\": {}\n },\n \"security\": {\n \"tool_guard\": {\n \"enabled\": true,\n \"shell_evasion_checks\": {\n \"command_substitution\": false,\n \"obfuscated_flags\": false,\n \"backslash_escaped_whitespace\": false,\n \"backslash_escaped_operators\": false,\n \"newlines\": false,\n \"comment_quote_desync\": false,\n \"quoted_newline\": false\n }\n },\n \"file_guard\": {\n \"enabled\": true\n },\n \"skill_scanner\": {\n \"mode\": \"warn\"\n },\n \"allow_no_auth_hosts\": [\"127.0.0.1\", \"::1\"]\n },\n \"last_dispatch\": null\n}\n```\n\n> **Note:** The complete field list and descriptions are provided in the sections below. Agent configuration can be managed in the Console or by directly editing the `agent.json` file.\n\n---\n\n### agent.json Field Reference\n\n#### `channels` — Messaging channel configs\n\nEach channel has common fields (like `enabled`, `bot_prefix`, access control policies, etc.) and channel-specific fields (like DingTalk's `client_id`, `client_secret`).\n\n**Supported channels:**\n\n- **console** — Console (enabled by default)\n- **dingtalk** — DingTalk\n- **feishu** — Feishu/Lark\n- **discord** — Discord\n- **telegram** — Telegram\n- **qq** — QQ bot\n- **imessage** — iMessage (macOS only)\n- **mattermost** — Mattermost\n- **matrix** — Matrix\n- **wecom** — WeCom (WeChat Work)\n- **wechat** — WeChat Personal (iLink)\n- **xiaoyi** — Huawei XiaoYi\n- **mqtt** — MQTT\n- **voice** — Voice\n\n> **Complete configuration:** Common fields, channel-specific fields (like DingTalk's `client_id`, Feishu's `app_id`), and detailed configuration steps for each channel are documented in [Channels](./channels).\n\nManagement: Console (Agent → Channels) or directly edit `agent.json`.\n\n> **Hot reload:** The system automatically detects `agent.json` changes every 2 seconds. After modifying channel config, it will auto-reload without restart.\n\n---\n\n#### `mcp` — MCP client configuration\n\nMCP (Model Context Protocol) allows agents to connect to external services (like Filesystem, Git, SQLite MCP servers, etc.).\n\nEach MCP client includes name, enabled state, transport method (stdio/HTTP/SSE), startup command or URL, and other fields.\n\n> **Complete configuration:** Full field descriptions, config formats, examples, and usage for MCP clients are documented in [MCP](./mcp).\n\nManagement: Console (Agent → MCP) or directly edit `agent.json`.\n\n---\n\n#### `heartbeat` — Heartbeat configuration\n\nHeartbeat is a scheduled self-check feature that executes tasks from `HEARTBEAT.md` at regular intervals.\n\n| Field | Type | Default | Description |\n| ---------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------ |\n| `enabled` | bool | `false` | Whether to enable heartbeat feature |\n| `every` | string | `\"30m\"` | Run interval. Supports `Nh`, `Nm`, `Ns` combos, e.g. `\"1h\"`, `\"30m\"`, `\"2h30m\"`, `\"90s\"` |\n| `target` | string | `\"main\"` | `\"main\"` = run in main session only; `\"last\"` = dispatch result to the last channel/user that sent a message |\n| `timeoutSeconds` | int | `300` | Maximum execution time for one heartbeat run, in seconds. Valid range: `1`–`3600` |\n| `activeHours` | object \\| null | `null` | Optional time window (if set, heartbeat only runs during this period) |\n\n**`heartbeat.activeHours`** (when not null):\n\n| Field | Type | Default | Description |\n| ------- | ------ | --------- | --------------------------- |\n| `start` | string | `\"08:00\"` | Start time (HH:MM, 24-hour) |\n| `end` | string | `\"22:00\"` | End time (HH:MM, 24-hour) |\n\nSee [Heartbeat](./heartbeat) for detailed guide.\n\n---\n\n#### `running` — Runtime configuration\n\nControls agent runtime behavior, retry strategies, context management, and memory configuration.\n\n**Basic Runtime:**\n\n| Field | Type | Default | Description |\n| ---------------------------- | ----- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `max_iters` | int | `100` | Maximum number of reasoning-acting iterations for ReAct agent (must be ≥ 1) |\n| `shell_command_timeout` | float | `60.0` | Default timeout in seconds for `execute_shell_command`. The LLM may still override this per-call via the timeout parameter |\n| `shell_command_executable` | str | `\"\"` | Path to the shell used by `execute_shell_command` on Unix/macOS (e.g. `/bin/bash`, `/bin/zsh`). On Windows, supports `powershell.exe` / `pwsh.exe`. When empty, falls back to `$SHELL`, then `/bin/sh` (or `cmd.exe` on Windows) |\n| `auto_continue_on_text_only` | bool | `false` | When enabled, the agent automatically retries up to two extra reasoning passes if the model responds with text but no tools |\n\n**LLM Retry & Rate Limiting:**\n\n| Field | Type | Default | Description |\n| ----------------------- | ----- | ------- | ----------------------------------------------------------------------------------------------------- |\n| `llm_retry_enabled` | bool | `true` | Whether to auto-retry transient LLM API failures such as rate limits, timeouts, and connection errors |\n| `llm_max_retries` | int | `3` | Maximum retry attempts for transient LLM API failures (must be ≥ 1) |\n| `llm_backoff_base` | float | `1.0` | Base delay in seconds for exponential retry backoff (must be ≥ 0.1) |\n| `llm_backoff_cap` | float | `10.0` | Maximum backoff delay cap in seconds (must be ≥ 0.5 and greater than or equal to `llm_backoff_base`) |\n| `llm_max_concurrent` | int | `10` | Maximum concurrent LLM calls (shared across all agents) |\n| `llm_max_qpm` | int | `600` | Maximum queries per minute (QPM). 0 = no limit |\n| `llm_rate_limit_pause` | float | `5.0` | Global pause duration in seconds after receiving a 429 rate limit response |\n| `llm_rate_limit_jitter` | float | `1.0` | Random jitter range in seconds added to rate limit pause to avoid thundering herd |\n| `llm_acquire_timeout` | float | `300.0` | Maximum timeout in seconds to wait for acquiring a rate limit slot |\n\n**Context Management:**\n\n| Field | Type | Default | Description |\n| -------------------------- | ------ | --------------- | ----------------------------------------------------------------------- |\n| `max_input_length` | int | `131072` (128K) | Maximum input length (tokens) for model context window (must be ≥ 1000) |\n| `history_max_length` | int | `10000` | Maximum output length (characters) for `/history` command |\n| `context_manager_backend` | string | `\"light\"` | Context manager backend type |\n| `memory_manager_backend` | string | `\"remelight\"` | Memory manager backend type |\n| `light_context_config` | object | _(see below)_ | Light context manager configuration |\n| `reme_light_memory_config` | object | _(see below)_ | ReMeLight memory manager configuration |\n\n**Light Context Configuration (`light_context_config` object):**\n\n| Field | Type | Default | Description |\n| ------------------------------ | ------ | ---------- | ------------------------------------------------------------ |\n| `dialog_path` | string | `\"dialog\"` | Dialog persistence directory (relative to working dir) |\n| `token_count_estimate_divisor` | float | `4.0` | Divisor for byte-based token estimation (byte_len / divisor) |\n\n**Light Context Compaction (`light_context_config.context_compact_config` object):**\n\n| Field | Type | Default | Description |\n| ------------------------- | ----- | ------- | ------------------------------------------------------------------------- |\n| `enabled` | bool | `true` | Whether to enable automatic context compaction |\n| `compact_threshold_ratio` | float | `0.8` | Threshold ratio (relative to `max_input_length`) that triggers compaction |\n| `reserve_threshold_ratio` | float | `0.1` | Ratio of recent context to preserve after compaction for continuity |\n\n**Light Tool Result Pruning (`light_context_config.tool_result_pruning_config` object):**\n\n| Field | Type | Default | Description |\n| ------------------------------ | ---- | ------- | ----------------------------------------------------------------------------------------------------------- |\n| `enabled` | bool | `true` | Whether to enable tool result pruning |\n| `pruning_recent_n` | int | `2` | Number of recent tool-result-bearing messages kept at the recent preview threshold before scroll compaction |\n| `pruning_old_msg_max_bytes` | int | `3000` | Compact preview byte threshold for tool results retained in live context after scroll compaction |\n| `pruning_recent_msg_max_bytes` | int | `50000` | Recent/execution preview byte threshold for tool results before and shortly after entering context |\n| `offload_retention_days` | int | `5` | Number of days to retain tool result files |\n\n**ReMeLight Memory Configuration (`reme_light_memory_config` object):**\n\n| Field | Type | Default | Description |\n| --------------------------- | ----------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `metadata_dir` | string | `\"mem_metadata\"` | Subdirectory for ReMe persistent state |\n| `session_dir` | string | `\"mem_session\"` | Subdirectory for ReMe source conversation logs used by auto-memory |\n| `mem_session_dir` | string | `\"mem_agent\"` | Subdirectory for ReMe internal memory-agent sessions |\n| `resource_dir` | string | `\"resource\"` | Subdirectory for external assets |\n| `daily_dir` | string | `\"memory\"` | Subdirectory for daily memory |\n| `digest_dir` | string | `\"digest\"` | Subdirectory for digest memory |\n| `summarize_when_compact` | bool | `true` | Whether to enable memory summarization during compaction |\n| `inbox_push_enabled` | bool | `true` | Whether to push auto-memory, auto-dream, and auto-resource job results to the inbox |\n| `auto_memory_interval` | int \\| null | `5` | Auto memory every N user queries. `None` or `<= 0` disables periodic auto memory |\n| `dream_cron_enabled` | bool | `true` | Whether to enable the scheduled dream-based memory optimization job |\n| `dream_cron` | string | `\"0 23 * * *\"` | Valid 5-field cron expression for dream-based memory optimization (required when enabled); scheduled runs start after a random delay of 0–60 seconds to avoid simultaneous calls |\n| `auto_memory_search_config` | object | _(see below)_ | Auto memory search configuration |\n| `embedding_model_config` | object | _(see below)_ | Embedding model configuration |\n\n> `rebuild_memory_index_on_start` is no longer supported. Rebuild an index only when needed from the Console or the\n> maintenance API described in [Rebuilding the Memory Search Index](./memory#rebuilding-the-memory-search-index).\n\n**Auto Memory Search Configuration (`reme_light_memory_config.auto_memory_search_config` object):**\n\n| Field | Type | Default | Description |\n| ------------- | ---- | ------- | -------------------------------------------------------- |\n| `enabled` | bool | `false` | Whether to auto search memory on every conversation turn |\n| `max_results` | int | `2` | Maximum results for auto memory search |\n\n**Embedding Configuration (`reme_light_memory_config.embedding_model_config` object):**\n\n| Field | Type | Default | Description |\n| ------------------ | ------ | ---------- | ---------------------------------------------------------------------------------------------- |\n| `backend` | string | `\"openai\"` | Embedding backend type: `openai`, `dashscope`, `dashscope_multimodal`, `gemini`, `ollama` |\n| `api_key` | string | `\"\"` | API key for the embedding provider. Required for OpenAI-compatible and Gemini backends |\n| `base_url` | string | `\"\"` | Optional custom API URL for OpenAI-compatible backends. For Ollama, this is passed as the host |\n| `model_name` | string | `\"\"` | Embedding model name (e.g., `\"text-embedding-3-small\"`) |\n| `dimensions` | int | `1024` | Embedding vector dimensions |\n| `enable_cache` | bool | `true` | Whether to enable embedding cache |\n| `use_dimensions` | bool | `false` | Whether to use custom dimensions |\n| `max_cache_size` | int | `10000` | Maximum cache size |\n| `max_input_length` | int | `8192` | Maximum input length for embeddings |\n| `max_batch_size` | int | `10` | Maximum batch size for batch processing |\n\nVector retrieval is enabled only when the selected backend has the minimum runnable configuration. These conditions are aligned with AgentScope credential requirements:\n\n| Backend | Enable condition | Credential mapping |\n| ----------------------------------------------- | --------------------------------------------- | ------------------------------- |\n| `openai` / `dashscope` / `dashscope_multimodal` | Both `model_name` and `api_key` are non-empty | `api_key`; optional `base_url` |\n| `gemini` | Both `model_name` and `api_key` are non-empty | `api_key` |\n| `ollama` | `model_name` is non-empty | optional `host` from `base_url` |\n\nWhen the enable condition is not met, ReMe still keeps keyword indexes and wikilink graph indexes, but the embedding vector index is disabled.\n\nThese settings can also be changed in the Console under **Agent → Runtime Config**. Fields read directly from\n`agent.json`, such as auto-memory cadence and auto-search limits, apply to later turns after saving. Embedded ReMe\ncomponent settings, such as directories and embedding configuration, require restarting the agent process so the ReMe\napplication is constructed with the new configuration.\n\n---\n\n#### `language` & `system_prompt_files` — Persona file configuration\n\n| Field | Type | Default | Description |\n| --------------------- | ------------- | ---------------------------------------- | ----------------------------------------------- |\n| `language` | string | `\"zh\"` | Agent language (`zh` / `en` / `ru`) |\n| `system_prompt_files` | array[string] | `[\"AGENTS.md\", \"SOUL.md\", \"PROFILE.md\"]` | List of persona files loaded into system prompt |\n\n**Persona files** define agent behavior and personality, stored in the workspace directory. You can:\n\n- Manage persona files in the Console's **Agent → Workspace** page (edit, enable/disable, reorder)\n- Directly edit the `system_prompt_files` array to control which files are loaded\n- Switch language in the Console's **Agent → Runtime Config** page (overwrites existing persona files)\n\n**Detailed explanation:** See [Agent Persona](./persona) documentation.\n\n---\n\n#### `user_timezone` — User timezone\n\n| Field | Type | Default | Description |\n| --------------- | ------ | ------------------- | ---------------------------------------------------------------------------------------------------------------------- |\n| `user_timezone` | string | _(system timezone)_ | IANA timezone name (e.g. `\"Asia/Shanghai\"`, `\"America/New_York\"`). Defaults to the system timezone detected at startup |\n\nThis timezone is used for:\n\n- Displaying the current time in the agent's system prompt\n- The `get_current_time` tool\n- Default timezone for new cron jobs (CLI and console)\n- Heartbeat active hours evaluation\n\nYou can also change it via the Console (Agent → Runtime Config).\n\n---\n\n#### `active_model` — Current model in use\n\nSpecifies the model used by this agent.\n\n| Field | Type | Default | Description |\n| ------------- | ------ | ------- | --------------------------------------------------- |\n| `provider_id` | string | `\"\"` | Model provider ID (e.g., `\"dashscope\"`, `\"openai\"`) |\n| `model` | string | `\"\"` | Model name (e.g., `\"qwen-max\"`, `\"gpt-4\"`) |\n\nWhen `null`, uses the global default model. Can be configured in Console (Agent → Model Settings).\n\n---\n\n#### `approval_level` — Tool execution security level\n\n| Field | Type | Default | Description |\n| ---------------- | ------ | -------- | ----------------------------------------------------------------------------------------------- |\n| `approval_level` | string | `\"AUTO\"` | Tool execution security level: `STRICT`, `SMART`, `AUTO`, or `OFF`. See [Security](./security). |\n\n---\n\n#### `tools` — Tool configuration\n\nControls the built-in tools available to the agent. Each tool can be individually enabled/disabled, configured whether to show to users, and whether to execute asynchronously.\n\n> **Complete configuration:** Detailed field structure, configuration examples, etc. for tools are documented in [MCP & Built-in Tools](./mcp).\n\nManagement: Console (Agent → Tool Config) or directly edit `agent.json`.\n\n---\n\n#### `security` — Security configuration\n\nContains three protection modules:\n\n- **`tool_guard`** — Tool guard (runtime detection of dangerous commands and injection attacks)\n- **`file_guard`** — File guard (protects sensitive file access)\n- **`skill_scanner`** — Skill scanner (scans for malicious code before enabling skills)\n\nTop-level field:\n\n| Field | Type | Default | Description |\n| --------------------- | -------- | ---------------------- | ------------------------------------------------------------------------------ |\n| `allow_no_auth_hosts` | string[] | `[\"127.0.0.1\", \"::1\"]` | IP whitelist that bypasses web authentication. Localhost is allowed by default |\n\n> **Complete configuration:** Detailed field descriptions, security rules, custom rule configuration, etc. for each module are documented in [Security](./security).\n\nManagement: Console (Settings → Security Config) or directly edit `agent.json`.\n\n---\n\n#### `last_dispatch` — Last message dispatch target\n\nRecords the last user message source, used for sending messages when heartbeat `target = \"last\"`.\n\n| Field | Type | Default | Description |\n| ------------ | ------ | ------- | --------------------------------------------- |\n| `channel` | string | `\"\"` | Channel name (e.g. `\"discord\"`, `\"dingtalk\"`) |\n| `user_id` | string | `\"\"` | User ID in that channel |\n| `session_id` | string | `\"\"` | Session/conversation ID |\n\nAuto-updated; no manual configuration needed.\n\n---\n\n## Model Providers\n\nQwenPaw needs an LLM provider to work. You can set it up in three ways:\n\n- **`qwenpaw init`** — interactive wizard, the easiest way\n- **Console UI** — in Settings → Models page\n- **API** — `PUT /providers/{id}` and `PUT /providers/active_llm`\n\n**Built-in providers:**\n\n| Provider | ID | Default Base URL | API Key Prefix |\n| ---------------------------------- | ------------------------ | --------------------------------------------------- | -------------- |\n| QwenPaw Local | `qwenpaw-local` | _(local)_ | _(none)_ |\n| Ollama | `ollama` | `http://localhost:11434` | _(none)_ |\n| LM Studio | `lmstudio` | `http://localhost:1234/v1` | _(none)_ |\n| OpenRouter | `openrouter` | `https://openrouter.ai/api/v1` | `sk-or-v1-` |\n| ModelScope | `modelscope` | `https://api-inference.modelscope.cn/v1` | `ms` |\n| DashScope | `dashscope` | `https://dashscope.aliyuncs.com/compatible-mode/v1` | `sk` |\n| Aliyun Coding Plan (China) | `aliyun-codingplan` | `https://coding.dashscope.aliyuncs.com/v1` | `sk-sp` |\n| Aliyun Coding Plan (International) | `aliyun-codingplan-intl` | `https://coding-intl.dashscope.aliyuncs.com/v1` | `sk-sp` |\n| OpenAI | `openai` | `https://api.openai.com/v1` | _(any)_ |\n| Azure OpenAI | `azure-openai` | _(you set it)_ | _(any)_ |\n| Anthropic | `anthropic` | `https://api.anthropic.com` | _(any)_ |\n| Google Gemini | `gemini` | `https://generativelanguage.googleapis.com` | _(any)_ |\n| DeepSeek | `deepseek` | `https://api.deepseek.com` | `sk-` |\n| Kimi (China) | `kimi-cn` | `https://api.moonshot.cn/v1` | _(any)_ |\n| Kimi (International) | `kimi-intl` | `https://api.moonshot.ai/v1` | _(any)_ |\n| MiniMax (China) | `minimax-cn` | `https://api.minimaxi.com/anthropic` | _(any)_ |\n| MiniMax (International) | `minimax` | `https://api.minimax.io/anthropic` | _(any)_ |\n| Zhipu (BigModel) | `zhipu-cn` | `https://open.bigmodel.cn/api/paas/v4` | _(any)_ |\n| Zhipu Coding Plan (BigModel) | `zhipu-cn-codingplan` | `https://open.bigmodel.cn/api/coding/paas/v4` | _(any)_ |\n| Zhipu (Z.AI) | `zhipu-intl` | `https://api.z.ai/api/paas/v4` | _(any)_ |\n| Zhipu Coding Plan (Z.AI) | `zhipu-intl-codingplan` | `https://api.z.ai/api/coding/paas/v4` | _(any)_ |\n| OpenCode | `opencode` | `https://opencode.ai/zen/v1` | _(any)_ |\n| SiliconFlow (China) | `siliconflow-cn` | `https://api.siliconflow.cn/v1` | `sk-` |\n| SiliconFlow (International) | `siliconflow-intl` | `https://api.siliconflow.com/v1` | `sk-` |\n| Custom | `custom` | _(you set it)_ | _(any)_ |\n\nFor each provider you need to set:\n\n| Setting | Description |\n| ---------- | ------------------------------------------------ |\n| `base_url` | API base URL (pre-filled for built-in providers) |\n| `api_key` | Your API key |\n\nThen choose which provider + model to activate:\n\n| Setting | Description |\n| ------------- | ---------------------------------------- |\n| `provider_id` | Which provider to use (e.g. `dashscope`) |\n| `model` | Which model to use (e.g. `qwen3-max`) |\n\n> **Tip:** Run `qwenpaw init` and follow the prompts — it will list available\n> models for each provider so you can pick one directly.\n>\n> **Note:** You are responsible for ensuring the API key and base URL are valid.\n> QwenPaw does not verify whether the key is correct or has sufficient quota —\n> make sure the chosen provider and model are accessible.\n\n---\n\n## Tool Environment Variables\n\nSome tools and MCP services need extra API keys (e.g. `TAVILY_API_KEY` for web search). You can\nmanage them in three ways:\n\n- **`qwenpaw init`** — prompts \"Configure environment variables?\" during setup\n- **Console UI** — edit on the settings page\n- **API** — `GET/PUT/DELETE /envs`\n\nSet variables are auto-loaded at app startup, so all tools and child processes\ncan read them via `os.environ`.\n\n> **Note:** You are responsible for ensuring the values (e.g. third-party API\n> keys) are valid. QwenPaw only stores and injects them — it does not verify\n> correctness.\n\n---\n\n## Skills\n\nSkills extend the agent's capabilities. Skill files are distributed across two locations:\n\n| Directory | Purpose |\n| ------------------------------------------ | ------------------------------------------------- |\n| `~/.qwenpaw/skill_pool/` | Local shared pool for built-ins and shared skills |\n| `~/.qwenpaw/workspaces/{agent_id}/skills/` | Skills present in a specific agent's workspace |\n\nEach skill is a directory with a `SKILL.md` file (YAML front matter with `name` and `description`), and optional `references/` and `scripts/` subdirectories.\n\nSkill enabled state and configuration are controlled by `~/.qwenpaw/workspaces/{agent_id}/skill.json`.\n\n**Manage skills via:**\n\n- Console (Agent → Skills) — Visual management, import, create, enable/disable\n- `qwenpaw init` (choose all / none / custom during setup)\n- `qwenpaw skills config` (interactive toggle)\n\nSee [Skills](./skills) for detailed documentation.\n\n---\n\n## Memory\n\nQwenPaw has persistent cross-conversation memory: it automatically compresses context and saves key information to Markdown files for long-term retention.\n\nMemory files are stored in the agent workspace:\n\n| File / Directory | Purpose |\n| ------------------------------------------------------- | --------------------------------------------------------------------- |\n| `~/.qwenpaw/workspaces/{agent_id}/MEMORY.md` | Long-lived key information (decisions, preferences, persistent facts) |\n| `~/.qwenpaw/workspaces/{agent_id}/memory/YYYY-MM-DD.md` | Daily logs (notes, runtime context, auto-generated summaries) |\n\n### Embedding Configuration\n\nMemory search relies on vector embeddings for semantic retrieval.\n\nConfigure embeddings in `agent.json` under `running.reme_light_memory_config.embedding_model_config`, which supports backend selection and parameters such as `use_dimensions`:\n\n> The vector-search enable condition is aligned with AgentScope credential requirements: OpenAI-compatible and Gemini backends require `model_name` plus `api_key`; Ollama only requires `model_name`. `base_url` is optional for OpenAI-compatible endpoints and is used as Ollama `host` when set. See [Memory](./memory#embedding-configuration-optional) for full configuration details.\n\n---\n\n## Summary\n\n- Everything lives under **`~/.qwenpaw`** by default; override with `QWENPAW_WORKING_DIR` (and related env vars) if needed.\n- From **v0.1.0**, configuration is split into:\n - **Global config** (`~/.qwenpaw/config.json`) — providers, environment variables, agent list\n - **Agent config** (`~/.qwenpaw/workspaces/{agent_id}/agent.json`) — per-agent settings\n- Daily management is primarily done through the **Console**, or by directly editing configuration files.\n- Agent personality is defined by Markdown files in the workspace directory. See [Agent Persona](./persona) for details.\n- LLM providers are globally configured via `qwenpaw init` or the Console.\n- Config changes are **auto-reloaded** without restart (polled every 2 seconds).\n- Call the Agent API: **POST** `/api/console/chat` with JSON body, SSE streaming; see [Quick start — Verify install](./quickstart#verify-install-optional) for examples.\n\n---\n\n## Related pages\n\n- [Introduction](./intro) — What the project can do\n- [Agent Persona](./persona) — Detailed explanation and management of persona files\n- [Channels](./channels) — How to configure messaging channels\n- [Heartbeat](./heartbeat) — Heartbeat configuration\n- [Multi-Agent](./multi-agent) — Multi-agent setup, management, and collaboration\n- [Memory](./memory) — Memory system details\n- [Skills](./skills) — Skills system details\n"}, "db8fb6391097791b08f63623ba4cd88421ec5cdb": {"t": 1785846834.138345, "n": 48521, "h": "958a9ab284dac0f8", "c": "# Config & Working Directory\n\nThis page covers:\n\n- **Directory structure** — Where files are stored and the purpose of each directory\n- **Environment variables** — How to customize paths and behavior\n- **Configuration files** — Complete field description for `config.json` and `agent.json`\n\nFrom **v0.1.0**, QwenPaw supports **multi-agent**. Configuration is split into two layers:\n\n1. **Global config** (`config.json`) — Model providers, agent list, global settings\n2. **Agent config** (`agent.json`) — Independent config for each agent (channels, heartbeat, tools, etc.)\n\n---\n\n## Directory Structure\n\nThe default working directory is `~/.qwenpaw`. After running `qwenpaw init`, the complete structure looks like:\n\n```\n$QWENPAW_WORKING_DIR/ # Default ~/.qwenpaw\n├── config.json # Global config\n├── workspaces/\n│ ├── default/ # Default agent workspace\n│ │ ├── agent.json # Agent config\n│ │ ├── chats.json # Conversation history\n│ │ ├── jobs.json # Cron jobs\n│ │ ├── token_usage.json # Token usage records\n│ │ ├── AGENTS.md # Persona file\n│ │ ├── SOUL.md # Persona file\n│ │ ├── PROFILE.md # Persona file\n│ │ ├── BOOTSTRAP.md # Initial setup guide (auto-deleted after completion)\n│ │ ├── MEMORY.md # Long-term memory\n│ │ ├── skills/ # Workspace-local skills\n│ │ ├── skill.json # Skill enabled state and config\n│ │ ├── memory/ # Daily memory files\n│ │ └── browser/ # Browser user data (cookies, cache, etc.)\n│ └── abc123/ # Other agent workspace\n│ └── ...\n└── skill_pool/ # Local shared skill pool\n ├── skill.json # Pool metadata\n └── ...\n\n$QWENPAW_SECRET_DIR/ # Default ~/.qwenpaw.secret\n├── providers.json # Model provider config and API keys\n└── envs.json # Environment variables\n```\n\n> **Path explanation:** `$QWENPAW_WORKING_DIR` and `$QWENPAW_SECRET_DIR` are environment variables, with default values of `~/.qwenpaw` and `~/.qwenpaw.secret` respectively. They can be customized via environment variables, see \"Environment Variables\" section below.\n\n### Directory Explanation\n\n**Global Directory (`~/.qwenpaw/`)**\n\n| File / Directory | Purpose |\n| ---------------- | ----------------------------------------------------- |\n| `config.json` | Global config (model providers, env vars, agent list) |\n| `workspaces/` | All agent workspace directories |\n\n**Agent Workspace (`~/.qwenpaw/workspaces/{agent_id}/`)**\n\n| File / Directory | Purpose |\n| ------------------ | ------------------------------------------------------------ |\n| `agent.json` | Agent config (channels, heartbeat, tools, skills, MCP, etc.) |\n| `chats.json` | Conversation history |\n| `jobs.json` | Cron job list |\n| `token_usage.json` | Token usage records |\n| `AGENTS.md` | Persona file (see [Agent Persona](./persona)) |\n| `SOUL.md` | Persona file (see [Agent Persona](./persona)) |\n| `PROFILE.md` | Persona file (see [Agent Persona](./persona)) |\n| `BOOTSTRAP.md` | Initial setup guide (auto-deleted after completion) |\n| `MEMORY.md` | Long-term memory (see [Memory](./memory)) |\n| `skills/` | Skills available in this workspace |\n| `skill.json` | Skill enabled state, channel routing, and config |\n| `memory/` | Daily memory files (see [Memory](./memory)) |\n| `browser/` | Browser user data (cookies, cache, localStorage, etc.) |\n\n> **Persona files:** Agent behavior and personality are defined by persona files. Running `qwenpaw init` automatically creates template files based on your chosen language (`zh` / `en` / `ru`). For detailed explanation and management, see [Agent Persona](./persona).\n\n> **Multi-Agent:** See the [Multi-Agent](./multi-agent) documentation for details.\n\n---\n\n## Environment Variables\n\nYou can customize paths and behavior via environment variables:\n\n**Path-related:**\n\n| Variable | Default | Description |\n| -------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `QWENPAW_WORKING_DIR` | `~/.qwenpaw` | Working directory root path |\n| `QWENPAW_SECRET_DIR` | `~/.qwenpaw.secret` | Sensitive data directory (stores `providers.json` and `envs.json`). Docker default is `/app/working.secret` |\n| `QWENPAW_KEYRING_ACCOUNT` | _(auto)_ | OS keychain account name for the master key. Defaults to `master_key`; when `QWENPAW_WORKING_DIR`/`QWENPAW_SECRET_DIR` are set (e.g. a dev checkout) it auto-derives a per-install account so a dev install never overwrites the stable install's key. Set explicitly to name a profile. |\n| `QWENPAW_CONFIG_FILE` | `config.json` | Config file name (relative to `QWENPAW_WORKING_DIR`) |\n| `QWENPAW_HEARTBEAT_FILE` | `HEARTBEAT.md` | Heartbeat file name (relative to agent workspace) |\n| `QWENPAW_JOBS_FILE` | `jobs.json` | Cron jobs file name (relative to agent workspace) |\n| `QWENPAW_CHATS_FILE` | `chats.json` | Conversation history file name (relative to agent workspace) |\n| `QWENPAW_TOKEN_USAGE_FILE` | `token_usage.json` | Token usage record file name (relative to agent workspace) |\n\n**Other configuration:**\n\n| Variable | Default | Description |\n| ------------------------------------ | --------------- | ---------------------------------------------------------------------------- |\n| `QWENPAW_LOG_LEVEL` | `info` | Log level (`debug` / `info` / `warning` / `error` / `critical`) |\n| `QWENPAW_LOG_MAX_SIZE` | `5MiB` | Maximum active log size; accepts bytes or suffixes such as `10MB` and `1GiB` |\n| `QWENPAW_LOG_MAX_BACKUPS` | `3` | Number of rotated log backups to retain; `0` disables backups |\n| `QWENPAW_MEMORY_COMPACT_THRESHOLD` | `100000` | Character threshold to trigger memory compaction |\n| `QWENPAW_MEMORY_COMPACT_KEEP_RECENT` | `3` | Number of recent messages to keep after compaction |\n| `QWENPAW_MEMORY_COMPACT_RATIO` | `0.7` | Threshold ratio for triggering compaction (relative to context window size) |\n| `QWENPAW_CONSOLE_STATIC_DIR` | _(auto-detect)_ | Console frontend static files path |\n\n**Security & Authentication:**\n\n| Variable | Default | Description |\n| ---------------------------- | ------- | -------------------------------------------------- |\n| `QWENPAW_AUTH_ENABLED` | `false` | Whether to enable Web console login authentication |\n| `QWENPAW_AUTH_USERNAME` | - | Admin username for auto-registration (optional) |\n| `QWENPAW_AUTH_PASSWORD` | - | Admin password for auto-registration (optional) |\n| `QWENPAW_TOOL_GUARD_ENABLED` | `true` | Whether to enable tool guard |\n| `QWENPAW_SKILL_SCAN_MODE` | `warn` | Skill scanning mode (`block` / `warn` / `off`) |\n\nExample — use a different working dir for this shell:\n\n```bash\nexport QWENPAW_WORKING_DIR=/home/me/my_qwenpaw\nqwenpaw app\n```\n\nConfig, HEARTBEAT, jobs, memory, etc. will be read/written under\n`/home/me/my_qwenpaw`.\n\n---\n\n## Configuration File Structure\n\nStarting from **v0.1.0**, configuration is split into two layers:\n\n1. **Global config** - `~/.qwenpaw/config.json` (providers, environment variables, agent list)\n2. **Agent config** - `~/.qwenpaw/workspaces/{agent_id}/agent.json` (per-agent settings)\n\n### Global config.json\n\nStores globally shared configuration:\n\n```json\n{\n \"agents\": {\n \"active_agent\": \"default\",\n \"profiles\": {\n \"default\": {\n \"id\": \"default\",\n \"name\": \"Default Agent\",\n \"description\": \"Default workspace agent\",\n \"enabled\": true\n },\n \"abc123\": {\n \"id\": \"abc123\",\n \"name\": \"Code Assistant\",\n \"description\": \"Focuses on code review and development\",\n \"enabled\": true\n }\n }\n },\n \"last_api\": {\n \"host\": \"127.0.0.1\",\n \"port\": 8088\n },\n \"show_tool_details\": true\n}\n```\n\n**Global config.json field descriptions:**\n\n| Field | Type | Default | Description |\n| --------------------- | -------------- | ------------------- | ----------------------------------------------------------------- |\n| `agents.active_agent` | string | `\"default\"` | Currently active agent ID |\n| `agents.profiles` | object | `{}` | Agent profile references (key is agent_id) |\n| `last_api.host` | string \\| null | `null` | Host address from last `qwenpaw app` start |\n| `last_api.port` | int \\| null | `null` | Port from last `qwenpaw app` start |\n| `show_tool_details` | bool | `true` | Whether to show tool call/return details in channel messages |\n| `user_timezone` | string | _(system timezone)_ | IANA timezone name (e.g., `\"Asia/Shanghai\"`) |\n| `last_dispatch` | object \\| null | `null` | Last message dispatch target (used for heartbeat `target=\"last\"`) |\n\n**`agents.profiles[agent_id]` reference fields:**\n\n| Field | Type | Required | Description |\n| --------------- | ------ | -------- | ----------------------------------------------------------------------------- |\n| `id` | string | Yes | Agent unique identifier |\n| `name` | string | Yes | Agent display name |\n| `description` | string | No | Agent description (used for multi-agent collaboration) |\n| `enabled` | bool | Yes | Whether to enable this agent |\n| `workspace_dir` | string | No | Workspace path (optional, defaults to `$QWENPAW_WORKING_DIR/workspaces/{id}`) |\n\n> **Backward compatibility:** The global config.json still supports `channels`, `mcp`, `tools`, `security` and other fields for backward compatibility with older versions. In multi-agent mode, these configurations should be set in each agent's `agent.json`.\n>\n> **Configuration priority:** The agent's `agent.json` takes precedence over the global `config.json`. When the same field is configured in both places, the system uses the value from `agent.json`. For multi-agent mode, it's recommended to put all configurations in each agent's `agent.json`.\n\n> **Model provider configuration** is stored in `$QWENPAW_SECRET_DIR/providers.json` (default `~/.qwenpaw.secret/providers.json`).\n> **Environment variables** are stored in `$QWENPAW_SECRET_DIR/envs.json` (default `~/.qwenpaw.secret/envs.json`).\n\n### Agent config (agent.json)\n\nEach agent has an independent `agent.json` in its workspace directory (`~/.qwenpaw/workspaces/{agent_id}/`) that stores all of its configuration (channels, tools, heartbeat, MCP, security, etc.). This allows different agents to have completely different configurations without interfering with each other.\n\n```json\n{\n \"id\": \"default\",\n \"name\": \"Default Agent\",\n \"description\": \"Default workspace agent\",\n \"workspace_dir\": \"\",\n \"channels\": {\n \"console\": {\n \"enabled\": true,\n \"bot_prefix\": \"\"\n },\n \"dingtalk\": {\n \"enabled\": false,\n \"bot_prefix\": \"\",\n \"client_id\": \"\",\n \"client_secret\": \"\"\n }\n },\n \"mcp\": {\n \"clients\": {\n \"filesystem\": {\n \"name\": \"Filesystem Access\",\n \"enabled\": true,\n \"command\": \"npx\",\n \"args\": [\n \"-y\",\n \"@modelcontextprotocol/server-filesystem\",\n \"/path/to/folder\"\n ]\n }\n }\n },\n \"heartbeat\": {\n \"enabled\": false,\n \"every\": \"30m\",\n \"target\": \"main\",\n \"timeoutSeconds\": 300,\n \"activeHours\": null\n },\n \"running\": {\n \"max_iters\": 50,\n \"llm_retry_enabled\": true,\n \"llm_max_retries\": 3,\n \"llm_backoff_base\": 1.0,\n \"llm_backoff_cap\": 10.0,\n \"max_input_length\": 131072\n },\n \"active_model\": null,\n \"language\": \"en\",\n \"system_prompt_files\": [\"AGENTS.md\", \"SOUL.md\", \"PROFILE.md\"],\n \"tools\": {\n \"builtin_tools\": {}\n },\n \"security\": {\n \"tool_guard\": {\n \"enabled\": true,\n \"shell_evasion_checks\": {\n \"command_substitution\": false,\n \"obfuscated_flags\": false,\n \"backslash_escaped_whitespace\": false,\n \"backslash_escaped_operators\": false,\n \"newlines\": false,\n \"comment_quote_desync\": false,\n \"quoted_newline\": false\n }\n },\n \"file_guard\": {\n \"enabled\": true\n },\n \"skill_scanner\": {\n \"mode\": \"warn\"\n },\n \"allow_no_auth_hosts\": [\"127.0.0.1\", \"::1\"]\n },\n \"last_dispatch\": null\n}\n```\n\n> **Note:** The complete field list and descriptions are provided in the sections below. Agent configuration can be managed in the Console or by directly editing the `agent.json` file.\n\n---\n\n### agent.json Field Reference\n\n#### `channels` — Messaging channel configs\n\nEach channel has common fields (like `enabled`, `bot_prefix`, access control policies, etc.) and channel-specific fields (like DingTalk's `client_id`, `client_secret`).\n\n**Supported channels:**\n\n- **console** — Console (enabled by default)\n- **dingtalk** — DingTalk\n- **feishu** — Feishu/Lark\n- **discord** — Discord\n- **telegram** — Telegram\n- **qq** — QQ bot\n- **imessage** — iMessage (macOS only)\n- **mattermost** — Mattermost\n- **matrix** — Matrix\n- **wecom** — WeCom (WeChat Work)\n- **wechat** — WeChat Personal (iLink)\n- **xiaoyi** — Huawei XiaoYi\n- **mqtt** — MQTT\n- **voice** — Voice\n\n> **Complete configuration:** Common fields, channel-specific fields (like DingTalk's `client_id`, Feishu's `app_id`), and detailed configuration steps for each channel are documented in [Channels](./channels).\n\nManagement: Console (Agent → Channels) or directly edit `agent.json`.\n\n> **Hot reload:** The system automatically detects `agent.json` changes every 2 seconds. After modifying channel config, it will auto-reload without restart.\n\n---\n\n#### `mcp` — MCP client configuration\n\nMCP (Model Context Protocol) allows agents to connect to external services (like Filesystem, Git, SQLite MCP servers, etc.).\n\nEach MCP client includes name, enabled state, transport method (stdio/HTTP/SSE), startup command or URL, and other fields.\n\n> **Complete configuration:** Full field descriptions, config formats, examples, and usage for MCP clients are documented in [MCP](./mcp).\n\nManagement: Console (Agent → MCP) or directly edit `agent.json`.\n\n---\n\n#### `heartbeat` — Heartbeat configuration\n\nHeartbeat is a scheduled self-check feature that executes tasks from `HEARTBEAT.md` at regular intervals.\n\n| Field | Type | Default | Description |\n| ---------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------ |\n| `enabled` | bool | `false` | Whether to enable heartbeat feature |\n| `every` | string | `\"30m\"` | Run interval. Supports `Nh`, `Nm`, `Ns` combos, e.g. `\"1h\"`, `\"30m\"`, `\"2h30m\"`, `\"90s\"` |\n| `target` | string | `\"main\"` | `\"main\"` = run in main session only; `\"last\"` = dispatch result to the last channel/user that sent a message |\n| `timeoutSeconds` | int | `300` | Maximum execution time for one heartbeat run, in seconds. Valid range: `1`–`3600` |\n| `activeHours` | object \\| null | `null` | Optional time window (if set, heartbeat only runs during this period) |\n\n**`heartbeat.activeHours`** (when not null):\n\n| Field | Type | Default | Description |\n| ------- | ------ | --------- | --------------------------- |\n| `start` | string | `\"08:00\"` | Start time (HH:MM, 24-hour) |\n| `end` | string | `\"22:00\"` | End time (HH:MM, 24-hour) |\n\nSee [Heartbeat](./heartbeat) for detailed guide.\n\n---\n\n#### `running` — Runtime configuration\n\nControls agent runtime behavior, retry strategies, context management, and memory configuration.\n\n**Basic Runtime:**\n\n| Field | Type | Default | Description |\n| ---------------------------- | ----- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `max_iters` | int | `100` | Maximum number of reasoning-acting iterations for ReAct agent (must be ≥ 1) |\n| `shell_command_timeout` | float | `60.0` | Default timeout in seconds for `execute_shell_command`. The LLM may still override this per-call via the timeout parameter |\n| `shell_command_executable` | str | `\"\"` | Path to the shell used by `execute_shell_command` on Unix/macOS (e.g. `/bin/bash`, `/bin/zsh`). On Windows, supports `powershell.exe` / `pwsh.exe`. When empty, falls back to `$SHELL`, then `/bin/sh` (or `cmd.exe` on Windows) |\n| `auto_continue_on_text_only` | bool | `false` | When enabled, the agent automatically retries up to two extra reasoning passes if the model responds with text but no tools |\n\n**LLM Retry & Rate Limiting:**\n\n| Field | Type | Default | Description |\n| ----------------------- | ----- | ------- | ----------------------------------------------------------------------------------------------------- |\n| `llm_retry_enabled` | bool | `true` | Whether to auto-retry transient LLM API failures such as rate limits, timeouts, and connection errors |\n| `llm_max_retries` | int | `3` | Maximum retry attempts for transient LLM API failures (must be ≥ 1) |\n| `llm_backoff_base` | float | `1.0` | Base delay in seconds for exponential retry backoff (must be ≥ 0.1) |\n| `llm_backoff_cap` | float | `10.0` | Maximum backoff delay cap in seconds (must be ≥ 0.5 and greater than or equal to `llm_backoff_base`) |\n| `llm_max_concurrent` | int | `10` | Maximum concurrent LLM calls (shared across all agents) |\n| `llm_max_qpm` | int | `600` | Maximum queries per minute (QPM). 0 = no limit |\n| `llm_rate_limit_pause` | float | `5.0` | Global pause duration in seconds after receiving a 429 rate limit response |\n| `llm_rate_limit_jitter` | float | `1.0` | Random jitter range in seconds added to rate limit pause to avoid thundering herd |\n| `llm_acquire_timeout` | float | `300.0` | Maximum timeout in seconds to wait for acquiring a rate limit slot |\n\n**Context Management:**\n\n| Field | Type | Default | Description |\n| -------------------------- | ------ | --------------- | ----------------------------------------------------------------------- |\n| `max_input_length` | int | `131072` (128K) | Maximum input length (tokens) for model context window (must be ≥ 1000) |\n| `history_max_length` | int | `10000` | Maximum output length (characters) for `/history` command |\n| `context_manager_backend` | string | `\"light\"` | Context manager backend type |\n| `memory_manager_backend` | string | `\"remelight\"` | Memory manager backend type |\n| `light_context_config` | object | _(see below)_ | Light context manager configuration |\n| `reme_light_memory_config` | object | _(see below)_ | ReMeLight memory manager configuration |\n\n**Light Context Configuration (`light_context_config` object):**\n\n| Field | Type | Default | Description |\n| ------------------------------ | ------ | ---------- | ------------------------------------------------------------ |\n| `dialog_path` | string | `\"dialog\"` | Dialog persistence directory (relative to working dir) |\n| `token_count_estimate_divisor` | float | `4.0` | Divisor for byte-based token estimation (byte_len / divisor) |\n\n**Light Context Compaction (`light_context_config.context_compact_config` object):**\n\n| Field | Type | Default | Description |\n| ------------------------- | ----- | ------- | ------------------------------------------------------------------------- |\n| `enabled` | bool | `true` | Whether to enable automatic context compaction |\n| `compact_threshold_ratio` | float | `0.8` | Threshold ratio (relative to `max_input_length`) that triggers compaction |\n| `reserve_threshold_ratio` | float | `0.1` | Ratio of recent context to preserve after compaction for continuity |\n\n**Light Tool Result Pruning (`light_context_config.tool_result_pruning_config` object):**\n\n| Field | Type | Default | Description |\n| ------------------------------ | ---- | ------- | ----------------------------------------------------------------------------------------------------------- |\n| `enabled` | bool | `true` | Whether to enable tool result pruning |\n| `pruning_recent_n` | int | `2` | Number of recent tool-result-bearing messages kept at the recent preview threshold before scroll compaction |\n| `pruning_old_msg_max_bytes` | int | `3000` | Compact preview byte threshold for tool results retained in live context after scroll compaction |\n| `pruning_recent_msg_max_bytes` | int | `50000` | Recent/execution preview byte threshold for tool results before and shortly after entering context |\n| `offload_retention_days` | int | `5` | Number of days to retain tool result files |\n\n**ReMeLight Memory Configuration (`reme_light_memory_config` object):**\n\n| Field | Type | Default | Description |\n| --------------------------- | ----------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `metadata_dir` | string | `\"mem_metadata\"` | Subdirectory for ReMe persistent state |\n| `session_dir` | string | `\"mem_session\"` | Subdirectory for ReMe source conversation logs used by auto-memory |\n| `mem_session_dir` | string | `\"mem_agent\"` | Subdirectory for ReMe internal memory-agent sessions |\n| `resource_dir` | string | `\"resource\"` | Subdirectory for external assets |\n| `daily_dir` | string | `\"memory\"` | Subdirectory for daily memory |\n| `digest_dir` | string | `\"digest\"` | Subdirectory for digest memory |\n| `summarize_when_compact` | bool | `true` | Whether to enable memory summarization during compaction |\n| `inbox_push_enabled` | bool | `true` | Whether to push auto-memory, auto-dream, and auto-resource job results to the inbox |\n| `auto_memory_interval` | int \\| null | `5` | Auto memory every N user queries. `None` or `<= 0` disables periodic auto memory |\n| `dream_cron_enabled` | bool | `true` | Whether to enable the scheduled dream-based memory optimization job |\n| `dream_cron` | string | `\"0 23 * * *\"` | Valid 5-field cron expression for dream-based memory optimization (required when enabled); scheduled runs start after a random delay of 0–60 seconds to avoid simultaneous calls |\n| `auto_memory_search_config` | object | _(see below)_ | Auto memory search configuration |\n| `embedding_model_config` | object | _(see below)_ | Embedding model configuration |\n\n> `rebuild_memory_index_on_start` is no longer supported. Rebuild an index only when needed from the Console or the\n> maintenance API described in [Rebuilding the Memory Search Index](./memory#rebuilding-the-memory-search-index).\n\n**Auto Memory Search Configuration (`reme_light_memory_config.auto_memory_search_config` object):**\n\n| Field | Type | Default | Description |\n| ------------- | ---- | ------- | -------------------------------------------------------- |\n| `enabled` | bool | `false` | Whether to auto search memory on every conversation turn |\n| `max_results` | int | `2` | Maximum results for auto memory search |\n\n**Embedding Configuration (`reme_light_memory_config.embedding_model_config` object):**\n\n| Field | Type | Default | Description |\n| ------------------ | ------ | ---------- | ---------------------------------------------------------------------------------------------- |\n| `backend` | string | `\"openai\"` | Embedding backend type: `openai`, `dashscope`, `dashscope_multimodal`, `gemini`, `ollama` |\n| `api_key` | string | `\"\"` | API key for the embedding provider. Required for OpenAI-compatible and Gemini backends |\n| `base_url` | string | `\"\"` | Optional custom API URL for OpenAI-compatible backends. For Ollama, this is passed as the host |\n| `model_name` | string | `\"\"` | Embedding model name (e.g., `\"text-embedding-3-small\"`) |\n| `dimensions` | int | `1024` | Embedding vector dimensions |\n| `enable_cache` | bool | `true` | Whether to enable embedding cache |\n| `use_dimensions` | bool | `false` | Whether to use custom dimensions |\n| `max_cache_size` | int | `10000` | Maximum cache size |\n| `max_input_length` | int | `8192` | Maximum input length for embeddings |\n| `max_batch_size` | int | `10` | Maximum batch size for batch processing |\n\nVector retrieval is enabled only when the selected backend has the minimum runnable configuration. These conditions are aligned with AgentScope credential requirements:\n\n| Backend | Enable condition | Credential mapping |\n| ----------------------------------------------- | --------------------------------------------- | ------------------------------- |\n| `openai` / `dashscope` / `dashscope_multimodal` | Both `model_name` and `api_key` are non-empty | `api_key`; optional `base_url` |\n| `gemini` | Both `model_name` and `api_key` are non-empty | `api_key` |\n| `ollama` | `model_name` is non-empty | optional `host` from `base_url` |\n\nWhen the enable condition is not met, ReMe still keeps keyword indexes and wikilink graph indexes, but the embedding vector index is disabled.\n\nThese settings can also be changed in the Console under **Agent → Runtime Config**. Fields read directly from\n`agent.json`, such as auto-memory cadence and auto-search limits, apply to later turns after saving. Embedded ReMe\ncomponent settings, such as directories and embedding configuration, require restarting the agent process so the ReMe\napplication is constructed with the new configuration.\n\n---\n\n#### `language` & `system_prompt_files` — Persona file configuration\n\n| Field | Type | Default | Description |\n| --------------------- | ------------- | ---------------------------------------- | ----------------------------------------------- |\n| `language` | string | `\"zh\"` | Agent language (`zh` / `en` / `ru`) |\n| `system_prompt_files` | array[string] | `[\"AGENTS.md\", \"SOUL.md\", \"PROFILE.md\"]` | List of persona files loaded into system prompt |\n\n**Persona files** define agent behavior and personality, stored in the workspace directory. You can:\n\n- Manage persona files in the Console's **Agent → Workspace** page (edit, enable/disable, reorder)\n- Directly edit the `system_prompt_files` array to control which files are loaded\n- Switch language in the Console's **Agent → Runtime Config** page (overwrites existing persona files)\n\n**Detailed explanation:** See [Agent Persona](./persona) documentation.\n\n---\n\n#### `user_timezone` — User timezone\n\n| Field | Type | Default | Description |\n| --------------- | ------ | ------------------- | ---------------------------------------------------------------------------------------------------------------------- |\n| `user_timezone` | string | _(system timezone)_ | IANA timezone name (e.g. `\"Asia/Shanghai\"`, `\"America/New_York\"`). Defaults to the system timezone detected at startup |\n\nThis timezone is used for:\n\n- Displaying the current time in the agent's system prompt\n- The `get_current_time` tool\n- Default timezone for new cron jobs (CLI and console)\n- Heartbeat active hours evaluation\n\nYou can also change it via the Console (Agent → Runtime Config).\n\n---\n\n#### `active_model` — Current model in use\n\nSpecifies the model used by this agent.\n\n| Field | Type | Default | Description |\n| ------------- | ------ | ------- | --------------------------------------------------- |\n| `provider_id` | string | `\"\"` | Model provider ID (e.g., `\"dashscope\"`, `\"openai\"`) |\n| `model` | string | `\"\"` | Model name (e.g., `\"qwen-max\"`, `\"gpt-4\"`) |\n\nWhen `null`, uses the global default model. Can be configured in Console (Agent → Model Settings).\n\n---\n\n#### `approval_level` — Tool execution security level\n\n| Field | Type | Default | Description |\n| ---------------- | ------ | -------- | ----------------------------------------------------------------------------------------------- |\n| `approval_level` | string | `\"AUTO\"` | Tool execution security level: `STRICT`, `SMART`, `AUTO`, or `OFF`. See [Security](./security). |\n\n---\n\n#### `tools` — Tool configuration\n\nControls the built-in tools available to the agent. Each tool can be individually enabled/disabled, configured whether to show to users, and whether to execute asynchronously.\n\n> **Complete configuration:** Detailed field structure, configuration examples, etc. for tools are documented in [MCP & Built-in Tools](./mcp).\n\nManagement: Console (Agent → Tool Config) or directly edit `agent.json`.\n\n---\n\n#### `security` — Security configuration\n\nContains three protection modules:\n\n- **`tool_guard`** — Tool guard (runtime detection of dangerous commands and injection attacks)\n- **`file_guard`** — File guard (protects sensitive file access)\n- **`skill_scanner`** — Skill scanner (scans for malicious code before enabling skills)\n\nTop-level field:\n\n| Field | Type | Default | Description |\n| --------------------- | -------- | ---------------------- | ------------------------------------------------------------------------------ |\n| `allow_no_auth_hosts` | string[] | `[\"127.0.0.1\", \"::1\"]` | IP whitelist that bypasses web authentication. Localhost is allowed by default |\n\n> **Complete configuration:** Detailed field descriptions, security rules, custom rule configuration, etc. for each module are documented in [Security](./security).\n\nManagement: Console (Settings → Security Config) or directly edit `agent.json`.\n\n---\n\n#### `last_dispatch` — Last message dispatch target\n\nRecords the last user message source, used for sending messages when heartbeat `target = \"last\"`.\n\n| Field | Type | Default | Description |\n| ------------ | ------ | ------- | --------------------------------------------- |\n| `channel` | string | `\"\"` | Channel name (e.g. `\"discord\"`, `\"dingtalk\"`) |\n| `user_id` | string | `\"\"` | User ID in that channel |\n| `session_id` | string | `\"\"` | Session/conversation ID |\n\nAuto-updated; no manual configuration needed.\n\n---\n\n## Model Providers\n\nQwenPaw needs an LLM provider to work. You can set it up in three ways:\n\n- **`qwenpaw init`** — interactive wizard, the easiest way\n- **Console UI** — in Settings → Models page\n- **API** — `PUT /providers/{id}` and `PUT /providers/active_llm`\n\n**Built-in providers:**\n\n| Provider | ID | Default Base URL | API Key Prefix |\n| ---------------------------------- | ------------------------ | --------------------------------------------------- | -------------- |\n| QwenPaw Local | `qwenpaw-local` | _(local)_ | _(none)_ |\n| Ollama | `ollama` | `http://localhost:11434` | _(none)_ |\n| LM Studio | `lmstudio` | `http://localhost:1234/v1` | _(none)_ |\n| OpenRouter | `openrouter` | `https://openrouter.ai/api/v1` | `sk-or-v1-` |\n| ModelScope | `modelscope` | `https://api-inference.modelscope.cn/v1` | `ms` |\n| DashScope | `dashscope` | `https://dashscope.aliyuncs.com/compatible-mode/v1` | `sk` |\n| Aliyun Coding Plan (China) | `aliyun-codingplan` | `https://coding.dashscope.aliyuncs.com/v1` | `sk-sp` |\n| Aliyun Coding Plan (International) | `aliyun-codingplan-intl` | `https://coding-intl.dashscope.aliyuncs.com/v1` | `sk-sp` |\n| OpenAI | `openai` | `https://api.openai.com/v1` | _(any)_ |\n| Azure OpenAI | `azure-openai` | _(you set it)_ | _(any)_ |\n| Anthropic | `anthropic` | `https://api.anthropic.com` | _(any)_ |\n| Google Gemini | `gemini` | `https://generativelanguage.googleapis.com` | _(any)_ |\n| DeepSeek | `deepseek` | `https://api.deepseek.com` | `sk-` |\n| Kimi (China) | `kimi-cn` | `https://api.moonshot.cn/v1` | _(any)_ |\n| Kimi (International) | `kimi-intl` | `https://api.moonshot.ai/v1` | _(any)_ |\n| MiniMax (China) | `minimax-cn` | `https://api.minimaxi.com/anthropic` | _(any)_ |\n| MiniMax (International) | `minimax` | `https://api.minimax.io/anthropic` | _(any)_ |\n| Zhipu (BigModel) | `zhipu-cn` | `https://open.bigmodel.cn/api/paas/v4` | _(any)_ |\n| Zhipu Coding Plan (BigModel) | `zhipu-cn-codingplan` | `https://open.bigmodel.cn/api/coding/paas/v4` | _(any)_ |\n| Zhipu (Z.AI) | `zhipu-intl` | `https://api.z.ai/api/paas/v4` | _(any)_ |\n| Zhipu Coding Plan (Z.AI) | `zhipu-intl-codingplan` | `https://api.z.ai/api/coding/paas/v4` | _(any)_ |\n| OpenCode | `opencode` | `https://opencode.ai/zen/v1` | _(any)_ |\n| SiliconFlow (China) | `siliconflow-cn` | `https://api.siliconflow.cn/v1` | `sk-` |\n| SiliconFlow (International) | `siliconflow-intl` | `https://api.siliconflow.com/v1` | `sk-` |\n| Custom | `custom` | _(you set it)_ | _(any)_ |\n\nFor each provider you need to set:\n\n| Setting | Description |\n| ---------- | ------------------------------------------------ |\n| `base_url` | API base URL (pre-filled for built-in providers) |\n| `api_key` | Your API key |\n\nThen choose which provider + model to activate:\n\n| Setting | Description |\n| ------------- | ---------------------------------------- |\n| `provider_id` | Which provider to use (e.g. `dashscope`) |\n| `model` | Which model to use (e.g. `qwen3-max`) |\n\n> **Tip:** Run `qwenpaw init` and follow the prompts — it will list available\n> models for each provider so you can pick one directly.\n>\n> **Note:** You are responsible for ensuring the API key and base URL are valid.\n> QwenPaw does not verify whether the key is correct or has sufficient quota —\n> make sure the chosen provider and model are accessible.\n\n---\n\n## Tool Environment Variables\n\nSome tools and MCP services need extra API keys (e.g. `TAVILY_API_KEY` for web search). You can\nmanage them in three ways:\n\n- **`qwenpaw init`** — prompts \"Configure environment variables?\" during setup\n- **Console UI** — edit on the settings page\n- **API** — `GET/PUT/DELETE /envs`\n\nSet variables are auto-loaded at app startup, so all tools and child processes\ncan read them via `os.environ`.\n\n> **Note:** You are responsible for ensuring the values (e.g. third-party API\n> keys) are valid. QwenPaw only stores and injects them — it does not verify\n> correctness.\n\n---\n\n## Skills\n\nSkills extend the agent's capabilities. Skill files are distributed across two locations:\n\n| Directory | Purpose |\n| ------------------------------------------ | ------------------------------------------------- |\n| `~/.qwenpaw/skill_pool/` | Local shared pool for built-ins and shared skills |\n| `~/.qwenpaw/workspaces/{agent_id}/skills/` | Skills present in a specific agent's workspace |\n\nEach skill is a directory with a `SKILL.md` file (YAML front matter with `name` and `description`), and optional `references/` and `scripts/` subdirectories.\n\nSkill enabled state and configuration are controlled by `~/.qwenpaw/workspaces/{agent_id}/skill.json`.\n\n**Manage skills via:**\n\n- Console (Agent → Skills) — Visual management, import, create, enable/disable\n- `qwenpaw init` (choose all / none / custom during setup)\n- `qwenpaw skills config` (interactive toggle)\n\nSee [Skills](./skills) for detailed documentation.\n\n---\n\n## Memory\n\nQwenPaw has persistent cross-conversation memory: it automatically compresses context and saves key information to Markdown files for long-term retention.\n\nMemory files are stored in the agent workspace:\n\n| File / Directory | Purpose |\n| ------------------------------------------------------- | --------------------------------------------------------------------- |\n| `~/.qwenpaw/workspaces/{agent_id}/MEMORY.md` | Long-lived key information (decisions, preferences, persistent facts) |\n| `~/.qwenpaw/workspaces/{agent_id}/memory/YYYY-MM-DD.md` | Daily logs (notes, runtime context, auto-generated summaries) |\n\n### Embedding Configuration\n\nMemory search relies on vector embeddings for semantic retrieval.\n\nConfigure embeddings in `agent.json` under `running.reme_light_memory_config.embedding_model_config`, which supports backend selection and parameters such as `use_dimensions`:\n\n> The vector-search enable condition is aligned with AgentScope credential requirements: OpenAI-compatible and Gemini backends require `model_name` plus `api_key`; Ollama only requires `model_name`. `base_url` is optional for OpenAI-compatible endpoints and is used as Ollama `host` when set. See [Memory](./memory#embedding-configuration-optional) for full configuration details.\n\n---\n\n## Summary\n\n- Everything lives under **`~/.qwenpaw`** by default; override with `QWENPAW_WORKING_DIR` (and related env vars) if needed.\n- From **v0.1.0**, configuration is split into:\n - **Global config** (`~/.qwenpaw/config.json`) — providers, environment variables, agent list\n - **Agent config** (`~/.qwenpaw/workspaces/{agent_id}/agent.json`) — per-agent settings\n- Daily management is primarily done through the **Console**, or by directly editing configuration files.\n- Agent personality is defined by Markdown files in the workspace directory. See [Agent Persona](./persona) for details.\n- LLM providers are globally configured via `qwenpaw init` or the Console.\n- Config changes are **auto-reloaded** without restart (polled every 2 seconds).\n- Call the Agent API: **POST** `/api/console/chat` with JSON body, SSE streaming; see [Quick start — Verify install](./quickstart#verify-install-optional) for examples.\n\n---\n\n## Related pages\n\n- [Introduction](./intro) — What the project can do\n- [Agent Persona](./persona) — Detailed explanation and management of persona files\n- [Channels](./channels) — How to configure messaging channels\n- [Heartbeat](./heartbeat) — Heartbeat configuration\n- [Multi-Agent](./multi-agent) — Multi-agent setup, management, and collaboration\n- [Memory](./memory) — Memory system details\n- [Skills](./skills) — Skills system details\n"}}, "src/qwenpaw/providers/openai_provider.py": {"e524dfe78d4a69b2441d8f611935bbe0b28cb7da": {"t": 1785674198.1591785, "n": 33203, "h": "e4f1d186ed046709", "c": "# -*- coding: utf-8 -*-\n\"\"\"An OpenAI provider implementation.\"\"\"\n\nfrom __future__ import annotations\n\nimport importlib.util\nimport json\nimport logging\nimport os\nimport re\nimport time\nfrom typing import TYPE_CHECKING, Any, List\nfrom urllib.parse import urlparse\n\nimport httpx\nfrom agentscope.model import ChatModelBase\nfrom openai import APIError\nfrom pydantic import Field\n\nfrom qwenpaw.providers.provider import ModelInfo, Provider\n\nfrom .capping_formatter import MAX_INLINE_MEDIA_BYTES, _CappingOpenAIFormatter\nfrom .multimodal_prober import evaluate_video_probe_answer\n\nif TYPE_CHECKING:\n from qwenpaw.providers.multimodal_prober import ProbeResult\n\nlogger = logging.getLogger(__name__)\n\nDASHSCOPE_BASE_URLS = (\n \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1\",\n \"https://dashscope-us.aliyuncs.com/compatible-mode/v1\",\n)\nCODING_DASHSCOPE_BASE_URL = \"https://coding.dashscope.aliyuncs.com/v1\"\nTOKEN_PLAN_BASE_URL = (\n \"https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1\"\n)\n\n# Exact hostnames of the DashScope OpenAI-compatible endpoints (mainland,\n# international and US), matched against the parsed URL hostname so\n# lookalike domains cannot select the DashScope probe.\n_DASHSCOPE_HOSTNAMES = frozenset(\n {\n \"dashscope.aliyuncs.com\",\n \"dashscope-intl.aliyuncs.com\",\n \"dashscope-us.aliyuncs.com\",\n },\n)\n\n\ndef _uses_max_completion_tokens(model_id: str) -> bool:\n \"\"\"Return whether an OpenAI model requires max_completion_tokens.\"\"\"\n model_name = model_id.strip().lower().rsplit(\"/\", maxsplit=1)[-1]\n return model_name.startswith(\"gpt-5\") or (\n len(model_name) > 1\n and model_name[0] == \"o\"\n and model_name[1].isdigit()\n )\n\n\ndef _token_limit_kwargs(model_id: str, limit: int) -> dict[str, int]:\n \"\"\"Build the model-specific output token limit argument.\"\"\"\n if _uses_max_completion_tokens(model_id):\n return {\"max_completion_tokens\": limit}\n return {\"max_tokens\": limit}\n\n\n# Model families that cannot be probed via a chat-completions \"ping\":\n# speech (ASR/TTS), embedding/rerank and image/video generation models\n# either require dedicated endpoints (e.g. DashScope async task APIs,\n# which reject chat calls with \"current user api does not support\n# asynchronous calls\") or reject text-only chat input.\n_NON_CHAT_TOKEN_KEYWORDS = frozenset(\n {\n \"asr\",\n \"tts\",\n \"t2v\",\n \"i2v\",\n \"s2v\",\n \"kf2v\",\n \"r2v\",\n \"t2i\",\n \"i2i\",\n \"sora\",\n },\n)\n_NON_CHAT_SUBSTRINGS = (\n \"paraformer\",\n \"sensevoice\",\n \"gummy\",\n \"cosyvoice\",\n \"sambert\",\n \"whisper\",\n \"wanx\",\n \"embedding\",\n \"rerank\",\n \"qwen-image\",\n \"z-image\",\n \"gpt-image\",\n \"dall-e\",\n \"video-synthesis\",\n \"image-synthesis\",\n \"seedance\",\n \"seedream\",\n \"seededit\",\n)\n\n# DashScope rejects chat calls to task-API models with these messages;\n# receiving one proves the endpoint recognised both the key and the model.\n_API_TYPE_MISMATCH_MARKERS = (\n \"does not support asynchronous calls\",\n \"does not support synchronous calls\",\n)\n\n# Official zero-cost validation endpoints for non-chat models. Neither\n# probe issues a billable inference/task request: the DashScope\n# upload-policy API only returns model-bound OSS credentials, and the\n# Ark task-list API only reads task history.\n_DASHSCOPE_UPLOAD_POLICY_PATH = \"/api/v1/uploads\"\n_ARK_TASK_LIST_PATH = \"/api/v3/contents/generations/tasks\"\n\n# Markers in a DashScope upload-policy error proving the model id is\n# unknown to the platform (vs. a model that merely lacks file input).\n_DASHSCOPE_UNKNOWN_MODEL_MARKERS = (\n \"not exist\",\n \"not found\",\n \"invalid model\",\n)\n\n\ndef _is_non_chat_model(model_id: str) -> bool:\n \"\"\"Return whether a model id looks like a non-chat model.\"\"\"\n name = model_id.strip().lower().rsplit(\"/\", maxsplit=1)[-1]\n if any(sub in name for sub in _NON_CHAT_SUBSTRINGS):\n return True\n tokens = set(re.split(r\"[^a-z0-9]+\", name))\n return not tokens.isdisjoint(_NON_CHAT_TOKEN_KEYWORDS)\n\n\ndef _http_error_detail(resp: httpx.Response) -> str:\n \"\"\"Extract a short human-readable error message from a response.\"\"\"\n try:\n payload = resp.json()\n except Exception:\n return (resp.text or \"\")[:300]\n if isinstance(payload, dict):\n error = payload.get(\"error\")\n if isinstance(error, dict):\n payload = error\n message = payload.get(\"message\") or payload.get(\"msg\")\n code = payload.get(\"code\")\n if message:\n return f\"{code}: {message}\" if code else str(message)\n return str(payload)[:300]\n\n\nif os.environ.get(\"LANGFUSE_SECRET_KEY\") and importlib.util.find_spec(\n \"langfuse\",\n):\n from langfuse.openai import AsyncOpenAI # type: ignore[import]\nelse:\n if os.environ.get(\"LANGFUSE_SECRET_KEY\"):\n logger.warning(\n \"LANGFUSE_SECRET_KEY is set but langfuse is not installed; \"\n \"install with `pip install langfuse` to enable tracing\",\n )\n from openai import AsyncOpenAI # pylint: disable=ungrouped-imports\n\n\nclass OpenAIProvider(Provider):\n \"\"\"Provider implementation for OpenAI API and compatible endpoints.\"\"\"\n\n max_inline_media_bytes: int = Field(\n default=MAX_INLINE_MEDIA_BYTES,\n ge=0,\n description=(\n \"Maximum size (in bytes) of a local media file inlined as \"\n \"base64 into the model request body. Media above this is \"\n \"replaced with a text placeholder to avoid oversized requests \"\n \"when large files (e.g. generated videos) persist in \"\n \"conversation history. 0 disables capping.\"\n ),\n )\n\n def _build_default_headers(self) -> dict:\n return dict(self.custom_headers) if self.custom_headers else {}\n\n def _client(self, timeout: float = 5) -> AsyncOpenAI:\n kwargs: dict = {\n \"base_url\": self.base_url,\n \"api_key\": self.api_key,\n \"timeout\": timeout,\n }\n headers = self._build_default_headers()\n if headers:\n kwargs[\"default_headers\"] = headers\n return AsyncOpenAI(**kwargs)\n\n @staticmethod\n def _normalize_models_payload(payload: Any) -> List[ModelInfo]:\n models: List[ModelInfo] = []\n rows = getattr(payload, \"data\", [])\n for row in rows or []:\n model_id = str(getattr(row, \"id\", \"\") or \"\").strip()\n if not model_id:\n continue\n model_name = (\n str(getattr(row, \"name\", \"\") or model_id).strip() or model_id\n )\n models.append(ModelInfo(id=model_id, name=model_name))\n\n deduped: List[ModelInfo] = []\n seen: set[str] = set()\n for model in models:\n if model.id in seen:\n continue\n seen.add(model.id)\n deduped.append(model)\n return deduped\n\n async def check_connection(self, timeout: float = 5) -> tuple[bool, str]:\n \"\"\"Check if OpenAI provider is reachable with current configuration.\"\"\"\n client = self._client()\n try:\n await client.models.list(timeout=timeout)\n return True, \"\"\n except APIError as exc:\n detail = str(exc) or getattr(exc, \"message\", \"\")\n status = getattr(exc, \"status_code\", \"unknown\")\n return (\n False,\n f\"API error when connecting to `{self.base_url}` \"\n f\"(status={status}): {detail}\",\n )\n except Exception as exc:\n return (\n False,\n f\"Unknown exception when connecting to `{self.base_url}`: \"\n f\"{exc}\",\n )\n\n async def fetch_models(self, timeout: float = 5) -> List[ModelInfo]:\n \"\"\"Fetch available models.\"\"\"\n try:\n client = self._client(timeout=timeout)\n payload = await client.models.list(timeout=timeout)\n models = self._normalize_models_payload(payload)\n return models\n except APIError:\n return []\n except Exception:\n return []\n\n async def check_model_connection(\n self,\n model_id: str,\n timeout: float = 5,\n ) -> tuple[bool, str]:\n \"\"\"Check if a specific model is reachable/usable\"\"\"\n model_id = (model_id or \"\").strip()\n if not model_id:\n return False, \"Empty model ID\"\n\n if _is_non_chat_model(model_id):\n # Non-chat models (ASR/TTS/embedding/media generation) cannot\n # answer a chat \"ping\", and issuing a real task against them\n # is expensive, so use the official zero-cost validation\n # endpoints instead.\n return await self._check_non_chat_model_connection(\n model_id,\n timeout,\n )\n\n try:\n client = self._client(timeout=timeout)\n res = await client.chat.completions.create(\n model=model_id,\n messages=[\n {\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"text\",\n \"text\": \"ping\",\n },\n ],\n },\n ],\n timeout=timeout,\n stream=True,\n **_token_limit_kwargs(model_id, 20),\n )\n # consume the stream to ensure the model is actually responsive\n async for _ in res:\n break\n return True, \"\"\n except APIError as exc:\n detail = str(exc) or getattr(exc, \"message\", \"\")\n if any(\n marker in detail.lower()\n for marker in _API_TYPE_MISMATCH_MARKERS\n ):\n # The endpoint recognised the key and the model but the\n # model must be invoked through its dedicated task API\n # (e.g. DashScope video generation), so connectivity is OK.\n return (\n True,\n f\"Model '{model_id}' requires a dedicated non-chat \"\n \"API; endpoint and credentials verified\",\n )\n status = getattr(exc, \"status_code\", \"unknown\")\n return (\n False,\n f\"API error when connecting to model '{model_id}' \"\n f\"(status={status}): {detail}\",\n )\n except Exception:\n return (\n False,\n f\"Unknown exception when connecting to model '{model_id}'\",\n )\n\n async def _check_non_chat_model_connection(\n self,\n model_id: str,\n timeout: float,\n ) -> tuple[bool, str]:\n \"\"\"Validate a non-chat model without issuing a billable request.\n\n * DashScope: the model-bound upload-policy API\n (``GET /api/v1/uploads?action=getPolicy&model=...``) verifies\n the endpoint, the API key and the model name at zero cost.\n * Volcano Ark: the content-generation task-list API\n (``GET /api/v3/contents/generations/tasks``) verifies the\n endpoint and the API key at zero cost.\n * Other providers: fall back to provider-level reachability.\n \"\"\"\n host = (urlparse(self.base_url).hostname or \"\").lower()\n if host in _DASHSCOPE_HOSTNAMES or host.endswith(\n \".dashscope.aliyuncs.com\",\n ):\n return await self._check_dashscope_non_chat_model(\n model_id,\n timeout,\n )\n if host == \"volces.com\" or host.endswith(\".volces.com\"):\n return await self._check_ark_non_chat_credentials(\n model_id,\n timeout,\n )\n ok, msg = await self.check_connection(timeout=timeout)\n if not ok:\n return False, msg\n return (\n True,\n f\"Model '{model_id}' is not a chat model; verified \"\n \"provider connectivity instead of a chat probe\",\n )\n\n async def _check_dashscope_non_chat_model(\n self,\n model_id: str,\n timeout: float,\n ) -> tuple[bool, str]:\n \"\"\"Probe a DashScope task-API model via the upload-policy API.\"\"\"\n parsed = urlparse(self.base_url)\n policy_url = (\n f\"{parsed.scheme}://{parsed.netloc}\"\n f\"{_DASHSCOPE_UPLOAD_POLICY_PATH}\"\n )\n try:\n async with httpx.AsyncClient(timeout=timeout) as client:\n resp = await client.get(\n policy_url,\n params={\"action\": \"getPolicy\", \"model\": model_id},\n headers={\"Authorization\": f\"Bearer {self.api_key}\"},\n )\n except Exception as exc:\n return False, f\"Failed to reach `{policy_url}`: {exc}\"\n if resp.status_code == 200:\n return (\n True,\n f\"Model '{model_id}' verified via the DashScope \"\n \"upload-policy API (endpoint, API key and model binding \"\n \"checked without a billable request)\",\n )\n detail = _http_error_detail(resp)\n if resp.status_code in (401, 403):\n return (\n False,\n f\"DashScope rejected the API key \"\n f\"(status={resp.status_code}): {detail}\",\n )\n lowered = detail.lower()\n if \"model\" in lowered and any(\n marker in lowered for marker in _DASHSCOPE_UNKNOWN_MODEL_MARKERS\n ):\n return (\n False,\n f\"DashScope does not recognise model '{model_id}' \"\n f\"(status={resp.status_code}): {detail}\",\n )\n if resp.status_code in (404, 408, 429) or resp.status_code >= 500:\n # These statuses prove nothing about the key: wrong base URL,\n # rate limiting or provider failure must surface as failures.\n return (\n False,\n f\"DashScope upload-policy probe failed \"\n f\"(status={resp.status_code}): {detail}\",\n )\n # Remaining 4xx: auth passed but the model cannot be probed via the\n # upload-policy API (e.g. TTS models without file input); the\n # endpoint and key are verified, which is the best zero-cost\n # signal available.\n return (\n True,\n f\"DashScope endpoint and API key verified; model \"\n f\"'{model_id}' was not invoked to avoid charges ({detail})\",\n )\n\n async def _check_ark_non_chat_credentials(\n self,\n model_id: str,\n timeout: float,\n ) -> tuple[bool, str]:\n \"\"\"Verify Ark endpoint and key via the free task-list API.\"\"\"\n parsed = urlparse(self.base_url)\n tasks_url = f\"{parsed.scheme}://{parsed.netloc}{_ARK_TASK_LIST_PATH}\"\n try:\n async with httpx.AsyncClient(timeout=timeout) as client:\n resp = await client.get(\n tasks_url,\n params={\"page_size\": 1},\n headers={\"Authorization\": f\"Bearer {self.api_key}\"},\n )\n except Exception as exc:\n return False, f\"Failed to reach `{tasks_url}`: {exc}\"\n if resp.status_code == 200:\n return (\n True,\n \"Ark endpoint and API key verified via the task-list \"\n f\"API; model '{model_id}' was not invoked to avoid \"\n \"charges\",\n )\n detail = _http_error_detail(resp)\n if resp.status_code in (401, 403):\n return (\n False,\n f\"Volcano Ark rejected the API key \"\n f\"(status={resp.status_code}): {detail}\",\n )\n # Endpoint variants (e.g. the coding plan) may not expose the\n # task-list API; fall back to provider-level reachability.\n ok, msg = await self.check_connection(timeout=timeout)\n if not ok:\n return False, msg\n return (\n True,\n f\"Model '{model_id}' is not a chat model; verified \"\n \"provider connectivity instead of a chat probe\",\n )\n\n def get_chat_model_instance(self, model_id: str) -> ChatModelBase:\n from agentscope.credential._openai import OpenAICredential\n from agentscope.model import OpenAIChatModel\n\n from .openai_chat_model_compat import OpenAIChatModelCompat\n\n credential = OpenAICredential(\n id=f\"qwenpaw-{self.id}\",\n api_key=self.api_key,\n base_url=self.base_url,\n )\n\n # Platform-specific headers injected per-request via extra_headers.\n merged_headers = self._build_default_headers()\n dashscope_meta = json.dumps(\n {\n \"agentType\": \"QwenPaw\",\n \"deployType\": \"UnKnown\",\n \"moduleCode\": \"model\",\n \"agentCode\": \"UnKnown\",\n },\n ensure_ascii=False,\n )\n if self.base_url in DASHSCOPE_BASE_URLS:\n merged_headers[\"x-dashscope-agentapp\"] = dashscope_meta\n elif self.base_url in (CODING_DASHSCOPE_BASE_URL, TOKEN_PLAN_BASE_URL):\n merged_headers[\"X-DashScope-Cdpl\"] = dashscope_meta\n\n gen_kwargs = self.get_effective_generate_kwargs(model_id)\n max_tokens = gen_kwargs.pop(\"max_tokens\", None)\n if _uses_max_completion_tokens(model_id):\n if max_tokens is not None:\n gen_kwargs.setdefault(\n \"max_completion_tokens\",\n max_tokens,\n )\n max_tokens = None\n parameters = OpenAIChatModel.Parameters(\n max_tokens=max_tokens,\n temperature=gen_kwargs.pop(\"temperature\", None),\n top_p=gen_kwargs.pop(\"top_p\", None),\n )\n\n return OpenAIChatModelCompat(\n credential=credential,\n model=model_id,\n parameters=parameters,\n stream=True,\n default_headers=merged_headers or None,\n extra_generate_kwargs=gen_kwargs or None,\n output_token_param=(\n \"max_completion_tokens\"\n if _uses_max_completion_tokens(model_id)\n else \"max_tokens\"\n ),\n context_size=self._get_context_size(model_id),\n formatter=_CappingOpenAIFormatter(\n max_bytes=self.max_inline_media_bytes,\n relay_reasoning_content=self._get_relay_reasoning(model_id),\n ),\n )\n\n async def probe_model_multimodal(\n self,\n model_id: str,\n timeout: float = 60,\n image_only: bool = False,\n ) -> ProbeResult:\n \"\"\"Probe multimodal support via OpenAI-compatible API.\"\"\"\n from .multimodal_prober import ProbeResult\n\n img_ok, img_msg = await self._probe_image_support(\n model_id,\n timeout,\n )\n # Skip video probe when image probe already failed: a model\n # that cannot perceive images will not perceive video either,\n # and some text-only models (e.g. qwen3-max) may randomly\n # guess the correct color keyword, causing false positives.\n if not img_ok:\n return ProbeResult(\n supports_image=False,\n supports_video=False,\n image_message=img_msg,\n video_message=\"Skipped: image probe failed\",\n )\n if image_only:\n return ProbeResult(\n supports_image=img_ok,\n supports_video=False,\n image_message=img_msg,\n video_message=\"Skipped: image_only=True\",\n )\n vid_ok, vid_msg = await self._probe_video_support(\n model_id,\n timeout,\n )\n return ProbeResult(\n supports_image=img_ok,\n supports_video=vid_ok,\n image_message=img_msg,\n video_message=vid_msg,\n )\n\n async def _probe_image_support(\n self,\n model_id: str,\n timeout: float = 15,\n ) -> tuple[bool, str]:\n \"\"\"Probe image support by sending a solid-red 16x16 PNG.\n\n Uses a two-stage check:\n 1. If the API rejects the request (400 / media-keyword error)\n → not supported.\n 2. If accepted, verify the model can *actually perceive* the\n image content via a semantic check\n (see ``evaluate_image_probe_answer``).\n\n Why a semantic check is necessary:\n Some models (e.g. qwen3-max via OpenAI-compatible API) silently\n accept image payloads without returning an error, yet they do NOT\n actually process the image — they simply ignore it and respond to\n the text prompt only. A pure \"did the API error?\" check would\n produce false positives for these models. The semantic check\n (asking for the dominant color and verifying the answer) catches\n this class of silent failures.\n \"\"\"\n from .multimodal_prober import (\n _IMAGE_PROBE_PROMPT,\n _PROBE_IMAGE_B64,\n _is_media_keyword_error,\n evaluate_image_probe_answer,\n )\n\n logger.info(\n \"Image probe start: model=%s url=%s\",\n model_id,\n self.base_url,\n )\n start_time = time.monotonic()\n client = self._client(timeout=timeout)\n try:\n res = await client.chat.completions.create(\n model=model_id,\n messages=[\n {\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"image_url\",\n \"image_url\": {\n \"url\": (\n \"data:image/png;base64,\"\n f\"{_PROBE_IMAGE_B64}\"\n ),\n },\n },\n {\n \"type\": \"text\",\n \"text\": _IMAGE_PROBE_PROMPT,\n },\n ],\n },\n ],\n timeout=timeout,\n **_token_limit_kwargs(model_id, 200),\n )\n answer = (res.choices[0].message.content or \"\").lower().strip()\n reasoning = \"\"\n msg = res.choices[0].message\n if hasattr(msg, \"reasoning_content\") and msg.reasoning_content:\n reasoning = msg.reasoning_content.lower()\n return evaluate_image_probe_answer(\n answer,\n model_id,\n start_time,\n reasoning,\n )\n except APIError as e:\n elapsed = time.monotonic() - start_time\n logger.warning(\n \"Image probe error: model=%s type=%s msg=%s %.2fs\",\n model_id,\n type(e).__name__,\n e,\n elapsed,\n )\n # 400 or media-keyword error → definitive rejection.\n # Other API errors are inconclusive (could be transient).\n # Use getattr because APITimeoutError lacks status_code.\n status = getattr(e, \"status_code\", None)\n if status == 400 or _is_media_keyword_error(e):\n return False, f\"Image not supported: {e}\"\n return False, f\"Probe inconclusive: {e}\"\n except Exception as e:\n elapsed = time.monotonic() - start_time\n logger.warning(\n \"Image probe error: model=%s type=%s msg=%s %.2fs\",\n model_id,\n type(e).__name__,\n e,\n elapsed,\n )\n return False, f\"Probe failed: {e}\"\n\n async def _probe_video_support(\n self,\n model_id: str,\n timeout: float = 30,\n ) -> tuple[bool, str]:\n \"\"\"Probe video support with automatic format fallback.\"\"\"\n from .multimodal_prober import _PROBE_VIDEO_B64, _PROBE_VIDEO_URL\n\n logger.info(\n \"Video probe start: model=%s url=%s\",\n model_id,\n self.base_url,\n )\n start_time = time.monotonic()\n video_urls = [\n f\"data:video/mp4;base64,{_PROBE_VIDEO_B64}\",\n _PROBE_VIDEO_URL,\n ]\n last_error_msg = \"\"\n for video_url in video_urls:\n result = await self._try_video_url(\n model_id,\n video_url,\n timeout,\n start_time=start_time,\n )\n if result is not None:\n return result\n last_error_msg = f\"format rejected for {video_url}\"\n elapsed = time.monotonic() - start_time\n logger.info(\n \"Video probe done: model=%s result=False %.2fs\",\n model_id,\n elapsed,\n )\n return False, f\"Video not supported: {last_error_msg}\"\n\n async def _try_video_url(\n self,\n model_id: str,\n video_url: str,\n timeout: float,\n *,\n start_time: float,\n ) -> tuple[bool, str] | None:\n \"\"\"Try a single video URL format. Return None to try next.\"\"\"\n from .multimodal_prober import (\n _PROBE_VIDEO_URL,\n _is_media_keyword_error,\n )\n\n is_http = video_url == _PROBE_VIDEO_URL\n req_timeout = timeout * 3 if is_http else timeout\n client = self._client(timeout=req_timeout)\n try:\n res = await client.chat.completions.create(\n model=model_id,\n messages=[\n {\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"video_url\",\n \"video_url\": {\"url\": video_url},\n },\n {\n \"type\": \"text\",\n \"text\": (\n \"What is the single dominant \"\n \"color shown in this video? \"\n \"Reply with ONLY the color \"\n \"name, nothing else.\"\n ),\n },\n ],\n },\n ],\n timeout=req_timeout,\n **_token_limit_kwargs(model_id, 200),\n )\n return self._evaluate_video_response(\n res,\n model_id,\n start_time,\n is_http,\n )\n except APIError as e:\n status = getattr(e, \"status_code\", None)\n # 400 means this specific video format was rejected, but the\n # model might accept a different format — return None to let\n # the caller try the next URL in the fallback list.\n if status == 400:\n logger.debug(\n \"Video probe format rejected (400): %s\",\n e,\n )\n return None\n elapsed = time.monotonic() - start_time\n # If the error message contains media-related keywords\n # (e.g. \"video\", \"vision\"), it's a definitive rejection.\n is_kw = _is_media_keyword_error(e)\n label = \"not supported\" if is_kw else \"inconclusive\"\n logger.warning(\n \"Video probe error: model=%s type=%s msg=%s %.2fs\",\n model_id,\n type(e).__name__,\n e,\n elapsed,\n )\n return False, f\"Video {label}: {e}\"\n except Exception as e:\n elapsed = time.monotonic() - start_time\n logger.warning(\n \"Video probe error: model=%s type=%s msg=%s %.2fs\",\n model_id,\n type(e).__name__,\n e,\n elapsed,\n )\n return False, f\"Probe failed: {e}\"\n\n @staticmethod\n def _evaluate_video_response(\n res,\n model_id: str,\n start_time: float,\n is_http: bool,\n ) -> tuple[bool, str]:\n \"\"\"Evaluate video probe response.\n\n Delegates to the shared\n ``evaluate_video_probe_answer`` in\n ``multimodal_prober`` so all providers use the same\n colour-keyword list and logging.\n \"\"\"\n answer = res.choices[0].message.content or \"\"\n reasoning = \"\"\n msg = res.choices[0].message\n if hasattr(msg, \"reasoning_content\") and msg.reasoning_content:\n reasoning = msg.reasoning_content\n return evaluate_video_probe_answer(\n answer,\n model_id,\n start_time,\n reasoning=reasoning,\n is_http=is_http,\n )\n\n\nclass _FreeSuffixProviderMixin:\n \"\"\"Mixin for providers that mark models as free by suffix.\"\"\"\n\n _FREE_SUFFIX = \"-free\"\n\n async def fetch_models(\n self,\n timeout: float = 5,\n ) -> List[ModelInfo]:\n \"\"\"Fetch models and mark free ones by suffix.\"\"\"\n try:\n client = self._client(timeout=timeout)\n payload = await client.models.list(timeout=timeout)\n except Exception:\n return []\n\n suffix = self._FREE_SUFFIX\n models: List[ModelInfo] = []\n seen: set[str] = set()\n for row in getattr(payload, \"data\", []) or []:\n model_id = str(\n getattr(row, \"id\", \"\") or \"\",\n ).strip()\n if not model_id or model_id in seen:\n continue\n seen.add(model_id)\n is_free = model_id.endswith(suffix)\n display_name = (\n model_id.removesuffix(suffix)\n .replace(\"-\", \" \")\n .replace(\"/\", \" - \")\n .title()\n )\n models.append(\n ModelInfo(\n id=model_id,\n name=display_name,\n is_free=is_free,\n ),\n )\n return models\n\n\nclass OpenCodeProvider(_FreeSuffixProviderMixin, OpenAIProvider):\n \"\"\"OpenCode provider with dynamic free model detection.\"\"\"\n\n _FREE_SUFFIX = \"-free\"\n\n\nclass KiloProvider(_FreeSuffixProviderMixin, OpenAIProvider):\n \"\"\"Kilo Code provider with dynamic free model detection.\"\"\"\n\n _FREE_SUFFIX = \":free\"\n\n\nclass GitHubModelsProvider(OpenAIProvider):\n \"\"\"GitHub Models provider.\n\n GitHub Models exposes an OpenAI-compatible chat completions endpoint at\n ``https://models.github.ai/inference``. Unlike many OpenAI-compatible\n providers it does **not** implement the ``/models`` listing endpoint, so\n the generic ``OpenAIProvider.check_connection`` (which calls\n ``client.models.list()``) receives a 404 response. This override checks\n connectivity by issuing a minimal chat completion request instead.\n \"\"\"\n\n async def check_connection(self, timeout: float = 5) -> tuple[bool, str]:\n \"\"\"Check connectivity via a tiny chat completion request.\"\"\"\n # Prefer a built-in model; fall back to a well-known GitHub Models id.\n model_id = \"\"\n for candidate in (\"openai/gpt-4o-mini\", \"gpt-4o-mini\"):\n if any(m.id == candidate for m in self.models):\n model_id = candidate\n break\n if not model_id:\n model_id = (\n self.models[0].id if self.models else \"openai/gpt-4o-mini\"\n )\n\n try:\n client = self._client(timeout=timeout)\n res = await client.chat.completions.create(\n model=model_id,\n messages=[\n {\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"text\",\n \"text\": \"ping\",\n },\n ],\n },\n ],\n timeout=timeout,\n stream=True,\n **_token_limit_kwargs(model_id, 5),\n )\n try:\n async for _ in res:\n break\n finally:\n await res.response.aclose()\n return True, \"\"\n except APIError as exc:\n detail = str(exc) or getattr(exc, \"message\", \"\")\n status = getattr(exc, \"status_code\", \"unknown\")\n return (\n False,\n f\"API error when connecting to `{self.base_url}` \"\n f\"(status={status}): {detail}\",\n )\n except Exception as exc:\n return (\n False,\n f\"Unknown exception when connecting to `{self.base_url}`: \"\n f\"{exc}\",\n )\n"}, "db8fb6391097791b08f63623ba4cd88421ec5cdb": {"t": 1785846847.2681713, "n": 33203, "h": "e4f1d186ed046709", "c": "# -*- coding: utf-8 -*-\n\"\"\"An OpenAI provider implementation.\"\"\"\n\nfrom __future__ import annotations\n\nimport importlib.util\nimport json\nimport logging\nimport os\nimport re\nimport time\nfrom typing import TYPE_CHECKING, Any, List\nfrom urllib.parse import urlparse\n\nimport httpx\nfrom agentscope.model import ChatModelBase\nfrom openai import APIError\nfrom pydantic import Field\n\nfrom qwenpaw.providers.provider import ModelInfo, Provider\n\nfrom .capping_formatter import MAX_INLINE_MEDIA_BYTES, _CappingOpenAIFormatter\nfrom .multimodal_prober import evaluate_video_probe_answer\n\nif TYPE_CHECKING:\n from qwenpaw.providers.multimodal_prober import ProbeResult\n\nlogger = logging.getLogger(__name__)\n\nDASHSCOPE_BASE_URLS = (\n \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1\",\n \"https://dashscope-us.aliyuncs.com/compatible-mode/v1\",\n)\nCODING_DASHSCOPE_BASE_URL = \"https://coding.dashscope.aliyuncs.com/v1\"\nTOKEN_PLAN_BASE_URL = (\n \"https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1\"\n)\n\n# Exact hostnames of the DashScope OpenAI-compatible endpoints (mainland,\n# international and US), matched against the parsed URL hostname so\n# lookalike domains cannot select the DashScope probe.\n_DASHSCOPE_HOSTNAMES = frozenset(\n {\n \"dashscope.aliyuncs.com\",\n \"dashscope-intl.aliyuncs.com\",\n \"dashscope-us.aliyuncs.com\",\n },\n)\n\n\ndef _uses_max_completion_tokens(model_id: str) -> bool:\n \"\"\"Return whether an OpenAI model requires max_completion_tokens.\"\"\"\n model_name = model_id.strip().lower().rsplit(\"/\", maxsplit=1)[-1]\n return model_name.startswith(\"gpt-5\") or (\n len(model_name) > 1\n and model_name[0] == \"o\"\n and model_name[1].isdigit()\n )\n\n\ndef _token_limit_kwargs(model_id: str, limit: int) -> dict[str, int]:\n \"\"\"Build the model-specific output token limit argument.\"\"\"\n if _uses_max_completion_tokens(model_id):\n return {\"max_completion_tokens\": limit}\n return {\"max_tokens\": limit}\n\n\n# Model families that cannot be probed via a chat-completions \"ping\":\n# speech (ASR/TTS), embedding/rerank and image/video generation models\n# either require dedicated endpoints (e.g. DashScope async task APIs,\n# which reject chat calls with \"current user api does not support\n# asynchronous calls\") or reject text-only chat input.\n_NON_CHAT_TOKEN_KEYWORDS = frozenset(\n {\n \"asr\",\n \"tts\",\n \"t2v\",\n \"i2v\",\n \"s2v\",\n \"kf2v\",\n \"r2v\",\n \"t2i\",\n \"i2i\",\n \"sora\",\n },\n)\n_NON_CHAT_SUBSTRINGS = (\n \"paraformer\",\n \"sensevoice\",\n \"gummy\",\n \"cosyvoice\",\n \"sambert\",\n \"whisper\",\n \"wanx\",\n \"embedding\",\n \"rerank\",\n \"qwen-image\",\n \"z-image\",\n \"gpt-image\",\n \"dall-e\",\n \"video-synthesis\",\n \"image-synthesis\",\n \"seedance\",\n \"seedream\",\n \"seededit\",\n)\n\n# DashScope rejects chat calls to task-API models with these messages;\n# receiving one proves the endpoint recognised both the key and the model.\n_API_TYPE_MISMATCH_MARKERS = (\n \"does not support asynchronous calls\",\n \"does not support synchronous calls\",\n)\n\n# Official zero-cost validation endpoints for non-chat models. Neither\n# probe issues a billable inference/task request: the DashScope\n# upload-policy API only returns model-bound OSS credentials, and the\n# Ark task-list API only reads task history.\n_DASHSCOPE_UPLOAD_POLICY_PATH = \"/api/v1/uploads\"\n_ARK_TASK_LIST_PATH = \"/api/v3/contents/generations/tasks\"\n\n# Markers in a DashScope upload-policy error proving the model id is\n# unknown to the platform (vs. a model that merely lacks file input).\n_DASHSCOPE_UNKNOWN_MODEL_MARKERS = (\n \"not exist\",\n \"not found\",\n \"invalid model\",\n)\n\n\ndef _is_non_chat_model(model_id: str) -> bool:\n \"\"\"Return whether a model id looks like a non-chat model.\"\"\"\n name = model_id.strip().lower().rsplit(\"/\", maxsplit=1)[-1]\n if any(sub in name for sub in _NON_CHAT_SUBSTRINGS):\n return True\n tokens = set(re.split(r\"[^a-z0-9]+\", name))\n return not tokens.isdisjoint(_NON_CHAT_TOKEN_KEYWORDS)\n\n\ndef _http_error_detail(resp: httpx.Response) -> str:\n \"\"\"Extract a short human-readable error message from a response.\"\"\"\n try:\n payload = resp.json()\n except Exception:\n return (resp.text or \"\")[:300]\n if isinstance(payload, dict):\n error = payload.get(\"error\")\n if isinstance(error, dict):\n payload = error\n message = payload.get(\"message\") or payload.get(\"msg\")\n code = payload.get(\"code\")\n if message:\n return f\"{code}: {message}\" if code else str(message)\n return str(payload)[:300]\n\n\nif os.environ.get(\"LANGFUSE_SECRET_KEY\") and importlib.util.find_spec(\n \"langfuse\",\n):\n from langfuse.openai import AsyncOpenAI # type: ignore[import]\nelse:\n if os.environ.get(\"LANGFUSE_SECRET_KEY\"):\n logger.warning(\n \"LANGFUSE_SECRET_KEY is set but langfuse is not installed; \"\n \"install with `pip install langfuse` to enable tracing\",\n )\n from openai import AsyncOpenAI # pylint: disable=ungrouped-imports\n\n\nclass OpenAIProvider(Provider):\n \"\"\"Provider implementation for OpenAI API and compatible endpoints.\"\"\"\n\n max_inline_media_bytes: int = Field(\n default=MAX_INLINE_MEDIA_BYTES,\n ge=0,\n description=(\n \"Maximum size (in bytes) of a local media file inlined as \"\n \"base64 into the model request body. Media above this is \"\n \"replaced with a text placeholder to avoid oversized requests \"\n \"when large files (e.g. generated videos) persist in \"\n \"conversation history. 0 disables capping.\"\n ),\n )\n\n def _build_default_headers(self) -> dict:\n return dict(self.custom_headers) if self.custom_headers else {}\n\n def _client(self, timeout: float = 5) -> AsyncOpenAI:\n kwargs: dict = {\n \"base_url\": self.base_url,\n \"api_key\": self.api_key,\n \"timeout\": timeout,\n }\n headers = self._build_default_headers()\n if headers:\n kwargs[\"default_headers\"] = headers\n return AsyncOpenAI(**kwargs)\n\n @staticmethod\n def _normalize_models_payload(payload: Any) -> List[ModelInfo]:\n models: List[ModelInfo] = []\n rows = getattr(payload, \"data\", [])\n for row in rows or []:\n model_id = str(getattr(row, \"id\", \"\") or \"\").strip()\n if not model_id:\n continue\n model_name = (\n str(getattr(row, \"name\", \"\") or model_id).strip() or model_id\n )\n models.append(ModelInfo(id=model_id, name=model_name))\n\n deduped: List[ModelInfo] = []\n seen: set[str] = set()\n for model in models:\n if model.id in seen:\n continue\n seen.add(model.id)\n deduped.append(model)\n return deduped\n\n async def check_connection(self, timeout: float = 5) -> tuple[bool, str]:\n \"\"\"Check if OpenAI provider is reachable with current configuration.\"\"\"\n client = self._client()\n try:\n await client.models.list(timeout=timeout)\n return True, \"\"\n except APIError as exc:\n detail = str(exc) or getattr(exc, \"message\", \"\")\n status = getattr(exc, \"status_code\", \"unknown\")\n return (\n False,\n f\"API error when connecting to `{self.base_url}` \"\n f\"(status={status}): {detail}\",\n )\n except Exception as exc:\n return (\n False,\n f\"Unknown exception when connecting to `{self.base_url}`: \"\n f\"{exc}\",\n )\n\n async def fetch_models(self, timeout: float = 5) -> List[ModelInfo]:\n \"\"\"Fetch available models.\"\"\"\n try:\n client = self._client(timeout=timeout)\n payload = await client.models.list(timeout=timeout)\n models = self._normalize_models_payload(payload)\n return models\n except APIError:\n return []\n except Exception:\n return []\n\n async def check_model_connection(\n self,\n model_id: str,\n timeout: float = 5,\n ) -> tuple[bool, str]:\n \"\"\"Check if a specific model is reachable/usable\"\"\"\n model_id = (model_id or \"\").strip()\n if not model_id:\n return False, \"Empty model ID\"\n\n if _is_non_chat_model(model_id):\n # Non-chat models (ASR/TTS/embedding/media generation) cannot\n # answer a chat \"ping\", and issuing a real task against them\n # is expensive, so use the official zero-cost validation\n # endpoints instead.\n return await self._check_non_chat_model_connection(\n model_id,\n timeout,\n )\n\n try:\n client = self._client(timeout=timeout)\n res = await client.chat.completions.create(\n model=model_id,\n messages=[\n {\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"text\",\n \"text\": \"ping\",\n },\n ],\n },\n ],\n timeout=timeout,\n stream=True,\n **_token_limit_kwargs(model_id, 20),\n )\n # consume the stream to ensure the model is actually responsive\n async for _ in res:\n break\n return True, \"\"\n except APIError as exc:\n detail = str(exc) or getattr(exc, \"message\", \"\")\n if any(\n marker in detail.lower()\n for marker in _API_TYPE_MISMATCH_MARKERS\n ):\n # The endpoint recognised the key and the model but the\n # model must be invoked through its dedicated task API\n # (e.g. DashScope video generation), so connectivity is OK.\n return (\n True,\n f\"Model '{model_id}' requires a dedicated non-chat \"\n \"API; endpoint and credentials verified\",\n )\n status = getattr(exc, \"status_code\", \"unknown\")\n return (\n False,\n f\"API error when connecting to model '{model_id}' \"\n f\"(status={status}): {detail}\",\n )\n except Exception:\n return (\n False,\n f\"Unknown exception when connecting to model '{model_id}'\",\n )\n\n async def _check_non_chat_model_connection(\n self,\n model_id: str,\n timeout: float,\n ) -> tuple[bool, str]:\n \"\"\"Validate a non-chat model without issuing a billable request.\n\n * DashScope: the model-bound upload-policy API\n (``GET /api/v1/uploads?action=getPolicy&model=...``) verifies\n the endpoint, the API key and the model name at zero cost.\n * Volcano Ark: the content-generation task-list API\n (``GET /api/v3/contents/generations/tasks``) verifies the\n endpoint and the API key at zero cost.\n * Other providers: fall back to provider-level reachability.\n \"\"\"\n host = (urlparse(self.base_url).hostname or \"\").lower()\n if host in _DASHSCOPE_HOSTNAMES or host.endswith(\n \".dashscope.aliyuncs.com\",\n ):\n return await self._check_dashscope_non_chat_model(\n model_id,\n timeout,\n )\n if host == \"volces.com\" or host.endswith(\".volces.com\"):\n return await self._check_ark_non_chat_credentials(\n model_id,\n timeout,\n )\n ok, msg = await self.check_connection(timeout=timeout)\n if not ok:\n return False, msg\n return (\n True,\n f\"Model '{model_id}' is not a chat model; verified \"\n \"provider connectivity instead of a chat probe\",\n )\n\n async def _check_dashscope_non_chat_model(\n self,\n model_id: str,\n timeout: float,\n ) -> tuple[bool, str]:\n \"\"\"Probe a DashScope task-API model via the upload-policy API.\"\"\"\n parsed = urlparse(self.base_url)\n policy_url = (\n f\"{parsed.scheme}://{parsed.netloc}\"\n f\"{_DASHSCOPE_UPLOAD_POLICY_PATH}\"\n )\n try:\n async with httpx.AsyncClient(timeout=timeout) as client:\n resp = await client.get(\n policy_url,\n params={\"action\": \"getPolicy\", \"model\": model_id},\n headers={\"Authorization\": f\"Bearer {self.api_key}\"},\n )\n except Exception as exc:\n return False, f\"Failed to reach `{policy_url}`: {exc}\"\n if resp.status_code == 200:\n return (\n True,\n f\"Model '{model_id}' verified via the DashScope \"\n \"upload-policy API (endpoint, API key and model binding \"\n \"checked without a billable request)\",\n )\n detail = _http_error_detail(resp)\n if resp.status_code in (401, 403):\n return (\n False,\n f\"DashScope rejected the API key \"\n f\"(status={resp.status_code}): {detail}\",\n )\n lowered = detail.lower()\n if \"model\" in lowered and any(\n marker in lowered for marker in _DASHSCOPE_UNKNOWN_MODEL_MARKERS\n ):\n return (\n False,\n f\"DashScope does not recognise model '{model_id}' \"\n f\"(status={resp.status_code}): {detail}\",\n )\n if resp.status_code in (404, 408, 429) or resp.status_code >= 500:\n # These statuses prove nothing about the key: wrong base URL,\n # rate limiting or provider failure must surface as failures.\n return (\n False,\n f\"DashScope upload-policy probe failed \"\n f\"(status={resp.status_code}): {detail}\",\n )\n # Remaining 4xx: auth passed but the model cannot be probed via the\n # upload-policy API (e.g. TTS models without file input); the\n # endpoint and key are verified, which is the best zero-cost\n # signal available.\n return (\n True,\n f\"DashScope endpoint and API key verified; model \"\n f\"'{model_id}' was not invoked to avoid charges ({detail})\",\n )\n\n async def _check_ark_non_chat_credentials(\n self,\n model_id: str,\n timeout: float,\n ) -> tuple[bool, str]:\n \"\"\"Verify Ark endpoint and key via the free task-list API.\"\"\"\n parsed = urlparse(self.base_url)\n tasks_url = f\"{parsed.scheme}://{parsed.netloc}{_ARK_TASK_LIST_PATH}\"\n try:\n async with httpx.AsyncClient(timeout=timeout) as client:\n resp = await client.get(\n tasks_url,\n params={\"page_size\": 1},\n headers={\"Authorization\": f\"Bearer {self.api_key}\"},\n )\n except Exception as exc:\n return False, f\"Failed to reach `{tasks_url}`: {exc}\"\n if resp.status_code == 200:\n return (\n True,\n \"Ark endpoint and API key verified via the task-list \"\n f\"API; model '{model_id}' was not invoked to avoid \"\n \"charges\",\n )\n detail = _http_error_detail(resp)\n if resp.status_code in (401, 403):\n return (\n False,\n f\"Volcano Ark rejected the API key \"\n f\"(status={resp.status_code}): {detail}\",\n )\n # Endpoint variants (e.g. the coding plan) may not expose the\n # task-list API; fall back to provider-level reachability.\n ok, msg = await self.check_connection(timeout=timeout)\n if not ok:\n return False, msg\n return (\n True,\n f\"Model '{model_id}' is not a chat model; verified \"\n \"provider connectivity instead of a chat probe\",\n )\n\n def get_chat_model_instance(self, model_id: str) -> ChatModelBase:\n from agentscope.credential._openai import OpenAICredential\n from agentscope.model import OpenAIChatModel\n\n from .openai_chat_model_compat import OpenAIChatModelCompat\n\n credential = OpenAICredential(\n id=f\"qwenpaw-{self.id}\",\n api_key=self.api_key,\n base_url=self.base_url,\n )\n\n # Platform-specific headers injected per-request via extra_headers.\n merged_headers = self._build_default_headers()\n dashscope_meta = json.dumps(\n {\n \"agentType\": \"QwenPaw\",\n \"deployType\": \"UnKnown\",\n \"moduleCode\": \"model\",\n \"agentCode\": \"UnKnown\",\n },\n ensure_ascii=False,\n )\n if self.base_url in DASHSCOPE_BASE_URLS:\n merged_headers[\"x-dashscope-agentapp\"] = dashscope_meta\n elif self.base_url in (CODING_DASHSCOPE_BASE_URL, TOKEN_PLAN_BASE_URL):\n merged_headers[\"X-DashScope-Cdpl\"] = dashscope_meta\n\n gen_kwargs = self.get_effective_generate_kwargs(model_id)\n max_tokens = gen_kwargs.pop(\"max_tokens\", None)\n if _uses_max_completion_tokens(model_id):\n if max_tokens is not None:\n gen_kwargs.setdefault(\n \"max_completion_tokens\",\n max_tokens,\n )\n max_tokens = None\n parameters = OpenAIChatModel.Parameters(\n max_tokens=max_tokens,\n temperature=gen_kwargs.pop(\"temperature\", None),\n top_p=gen_kwargs.pop(\"top_p\", None),\n )\n\n return OpenAIChatModelCompat(\n credential=credential,\n model=model_id,\n parameters=parameters,\n stream=True,\n default_headers=merged_headers or None,\n extra_generate_kwargs=gen_kwargs or None,\n output_token_param=(\n \"max_completion_tokens\"\n if _uses_max_completion_tokens(model_id)\n else \"max_tokens\"\n ),\n context_size=self._get_context_size(model_id),\n formatter=_CappingOpenAIFormatter(\n max_bytes=self.max_inline_media_bytes,\n relay_reasoning_content=self._get_relay_reasoning(model_id),\n ),\n )\n\n async def probe_model_multimodal(\n self,\n model_id: str,\n timeout: float = 60,\n image_only: bool = False,\n ) -> ProbeResult:\n \"\"\"Probe multimodal support via OpenAI-compatible API.\"\"\"\n from .multimodal_prober import ProbeResult\n\n img_ok, img_msg = await self._probe_image_support(\n model_id,\n timeout,\n )\n # Skip video probe when image probe already failed: a model\n # that cannot perceive images will not perceive video either,\n # and some text-only models (e.g. qwen3-max) may randomly\n # guess the correct color keyword, causing false positives.\n if not img_ok:\n return ProbeResult(\n supports_image=False,\n supports_video=False,\n image_message=img_msg,\n video_message=\"Skipped: image probe failed\",\n )\n if image_only:\n return ProbeResult(\n supports_image=img_ok,\n supports_video=False,\n image_message=img_msg,\n video_message=\"Skipped: image_only=True\",\n )\n vid_ok, vid_msg = await self._probe_video_support(\n model_id,\n timeout,\n )\n return ProbeResult(\n supports_image=img_ok,\n supports_video=vid_ok,\n image_message=img_msg,\n video_message=vid_msg,\n )\n\n async def _probe_image_support(\n self,\n model_id: str,\n timeout: float = 15,\n ) -> tuple[bool, str]:\n \"\"\"Probe image support by sending a solid-red 16x16 PNG.\n\n Uses a two-stage check:\n 1. If the API rejects the request (400 / media-keyword error)\n → not supported.\n 2. If accepted, verify the model can *actually perceive* the\n image content via a semantic check\n (see ``evaluate_image_probe_answer``).\n\n Why a semantic check is necessary:\n Some models (e.g. qwen3-max via OpenAI-compatible API) silently\n accept image payloads without returning an error, yet they do NOT\n actually process the image — they simply ignore it and respond to\n the text prompt only. A pure \"did the API error?\" check would\n produce false positives for these models. The semantic check\n (asking for the dominant color and verifying the answer) catches\n this class of silent failures.\n \"\"\"\n from .multimodal_prober import (\n _IMAGE_PROBE_PROMPT,\n _PROBE_IMAGE_B64,\n _is_media_keyword_error,\n evaluate_image_probe_answer,\n )\n\n logger.info(\n \"Image probe start: model=%s url=%s\",\n model_id,\n self.base_url,\n )\n start_time = time.monotonic()\n client = self._client(timeout=timeout)\n try:\n res = await client.chat.completions.create(\n model=model_id,\n messages=[\n {\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"image_url\",\n \"image_url\": {\n \"url\": (\n \"data:image/png;base64,\"\n f\"{_PROBE_IMAGE_B64}\"\n ),\n },\n },\n {\n \"type\": \"text\",\n \"text\": _IMAGE_PROBE_PROMPT,\n },\n ],\n },\n ],\n timeout=timeout,\n **_token_limit_kwargs(model_id, 200),\n )\n answer = (res.choices[0].message.content or \"\").lower().strip()\n reasoning = \"\"\n msg = res.choices[0].message\n if hasattr(msg, \"reasoning_content\") and msg.reasoning_content:\n reasoning = msg.reasoning_content.lower()\n return evaluate_image_probe_answer(\n answer,\n model_id,\n start_time,\n reasoning,\n )\n except APIError as e:\n elapsed = time.monotonic() - start_time\n logger.warning(\n \"Image probe error: model=%s type=%s msg=%s %.2fs\",\n model_id,\n type(e).__name__,\n e,\n elapsed,\n )\n # 400 or media-keyword error → definitive rejection.\n # Other API errors are inconclusive (could be transient).\n # Use getattr because APITimeoutError lacks status_code.\n status = getattr(e, \"status_code\", None)\n if status == 400 or _is_media_keyword_error(e):\n return False, f\"Image not supported: {e}\"\n return False, f\"Probe inconclusive: {e}\"\n except Exception as e:\n elapsed = time.monotonic() - start_time\n logger.warning(\n \"Image probe error: model=%s type=%s msg=%s %.2fs\",\n model_id,\n type(e).__name__,\n e,\n elapsed,\n )\n return False, f\"Probe failed: {e}\"\n\n async def _probe_video_support(\n self,\n model_id: str,\n timeout: float = 30,\n ) -> tuple[bool, str]:\n \"\"\"Probe video support with automatic format fallback.\"\"\"\n from .multimodal_prober import _PROBE_VIDEO_B64, _PROBE_VIDEO_URL\n\n logger.info(\n \"Video probe start: model=%s url=%s\",\n model_id,\n self.base_url,\n )\n start_time = time.monotonic()\n video_urls = [\n f\"data:video/mp4;base64,{_PROBE_VIDEO_B64}\",\n _PROBE_VIDEO_URL,\n ]\n last_error_msg = \"\"\n for video_url in video_urls:\n result = await self._try_video_url(\n model_id,\n video_url,\n timeout,\n start_time=start_time,\n )\n if result is not None:\n return result\n last_error_msg = f\"format rejected for {video_url}\"\n elapsed = time.monotonic() - start_time\n logger.info(\n \"Video probe done: model=%s result=False %.2fs\",\n model_id,\n elapsed,\n )\n return False, f\"Video not supported: {last_error_msg}\"\n\n async def _try_video_url(\n self,\n model_id: str,\n video_url: str,\n timeout: float,\n *,\n start_time: float,\n ) -> tuple[bool, str] | None:\n \"\"\"Try a single video URL format. Return None to try next.\"\"\"\n from .multimodal_prober import (\n _PROBE_VIDEO_URL,\n _is_media_keyword_error,\n )\n\n is_http = video_url == _PROBE_VIDEO_URL\n req_timeout = timeout * 3 if is_http else timeout\n client = self._client(timeout=req_timeout)\n try:\n res = await client.chat.completions.create(\n model=model_id,\n messages=[\n {\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"video_url\",\n \"video_url\": {\"url\": video_url},\n },\n {\n \"type\": \"text\",\n \"text\": (\n \"What is the single dominant \"\n \"color shown in this video? \"\n \"Reply with ONLY the color \"\n \"name, nothing else.\"\n ),\n },\n ],\n },\n ],\n timeout=req_timeout,\n **_token_limit_kwargs(model_id, 200),\n )\n return self._evaluate_video_response(\n res,\n model_id,\n start_time,\n is_http,\n )\n except APIError as e:\n status = getattr(e, \"status_code\", None)\n # 400 means this specific video format was rejected, but the\n # model might accept a different format — return None to let\n # the caller try the next URL in the fallback list.\n if status == 400:\n logger.debug(\n \"Video probe format rejected (400): %s\",\n e,\n )\n return None\n elapsed = time.monotonic() - start_time\n # If the error message contains media-related keywords\n # (e.g. \"video\", \"vision\"), it's a definitive rejection.\n is_kw = _is_media_keyword_error(e)\n label = \"not supported\" if is_kw else \"inconclusive\"\n logger.warning(\n \"Video probe error: model=%s type=%s msg=%s %.2fs\",\n model_id,\n type(e).__name__,\n e,\n elapsed,\n )\n return False, f\"Video {label}: {e}\"\n except Exception as e:\n elapsed = time.monotonic() - start_time\n logger.warning(\n \"Video probe error: model=%s type=%s msg=%s %.2fs\",\n model_id,\n type(e).__name__,\n e,\n elapsed,\n )\n return False, f\"Probe failed: {e}\"\n\n @staticmethod\n def _evaluate_video_response(\n res,\n model_id: str,\n start_time: float,\n is_http: bool,\n ) -> tuple[bool, str]:\n \"\"\"Evaluate video probe response.\n\n Delegates to the shared\n ``evaluate_video_probe_answer`` in\n ``multimodal_prober`` so all providers use the same\n colour-keyword list and logging.\n \"\"\"\n answer = res.choices[0].message.content or \"\"\n reasoning = \"\"\n msg = res.choices[0].message\n if hasattr(msg, \"reasoning_content\") and msg.reasoning_content:\n reasoning = msg.reasoning_content\n return evaluate_video_probe_answer(\n answer,\n model_id,\n start_time,\n reasoning=reasoning,\n is_http=is_http,\n )\n\n\nclass _FreeSuffixProviderMixin:\n \"\"\"Mixin for providers that mark models as free by suffix.\"\"\"\n\n _FREE_SUFFIX = \"-free\"\n\n async def fetch_models(\n self,\n timeout: float = 5,\n ) -> List[ModelInfo]:\n \"\"\"Fetch models and mark free ones by suffix.\"\"\"\n try:\n client = self._client(timeout=timeout)\n payload = await client.models.list(timeout=timeout)\n except Exception:\n return []\n\n suffix = self._FREE_SUFFIX\n models: List[ModelInfo] = []\n seen: set[str] = set()\n for row in getattr(payload, \"data\", []) or []:\n model_id = str(\n getattr(row, \"id\", \"\") or \"\",\n ).strip()\n if not model_id or model_id in seen:\n continue\n seen.add(model_id)\n is_free = model_id.endswith(suffix)\n display_name = (\n model_id.removesuffix(suffix)\n .replace(\"-\", \" \")\n .replace(\"/\", \" - \")\n .title()\n )\n models.append(\n ModelInfo(\n id=model_id,\n name=display_name,\n is_free=is_free,\n ),\n )\n return models\n\n\nclass OpenCodeProvider(_FreeSuffixProviderMixin, OpenAIProvider):\n \"\"\"OpenCode provider with dynamic free model detection.\"\"\"\n\n _FREE_SUFFIX = \"-free\"\n\n\nclass KiloProvider(_FreeSuffixProviderMixin, OpenAIProvider):\n \"\"\"Kilo Code provider with dynamic free model detection.\"\"\"\n\n _FREE_SUFFIX = \":free\"\n\n\nclass GitHubModelsProvider(OpenAIProvider):\n \"\"\"GitHub Models provider.\n\n GitHub Models exposes an OpenAI-compatible chat completions endpoint at\n ``https://models.github.ai/inference``. Unlike many OpenAI-compatible\n providers it does **not** implement the ``/models`` listing endpoint, so\n the generic ``OpenAIProvider.check_connection`` (which calls\n ``client.models.list()``) receives a 404 response. This override checks\n connectivity by issuing a minimal chat completion request instead.\n \"\"\"\n\n async def check_connection(self, timeout: float = 5) -> tuple[bool, str]:\n \"\"\"Check connectivity via a tiny chat completion request.\"\"\"\n # Prefer a built-in model; fall back to a well-known GitHub Models id.\n model_id = \"\"\n for candidate in (\"openai/gpt-4o-mini\", \"gpt-4o-mini\"):\n if any(m.id == candidate for m in self.models):\n model_id = candidate\n break\n if not model_id:\n model_id = (\n self.models[0].id if self.models else \"openai/gpt-4o-mini\"\n )\n\n try:\n client = self._client(timeout=timeout)\n res = await client.chat.completions.create(\n model=model_id,\n messages=[\n {\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"text\",\n \"text\": \"ping\",\n },\n ],\n },\n ],\n timeout=timeout,\n stream=True,\n **_token_limit_kwargs(model_id, 5),\n )\n try:\n async for _ in res:\n break\n finally:\n await res.response.aclose()\n return True, \"\"\n except APIError as exc:\n detail = str(exc) or getattr(exc, \"message\", \"\")\n status = getattr(exc, \"status_code\", \"unknown\")\n return (\n False,\n f\"API error when connecting to `{self.base_url}` \"\n f\"(status={status}): {detail}\",\n )\n except Exception as exc:\n return (\n False,\n f\"Unknown exception when connecting to `{self.base_url}`: \"\n f\"{exc}\",\n )\n"}}, "console/src/locales/ja.json": {"e524dfe78d4a69b2441d8f611935bbe0b28cb7da": {"t": 1785674212.4189782, "n": 134779, "h": "a18619ab9daa0fd6", "c": "{\n \"common\": {\n \"save\": \"保存\",\n \"reset\": \"リセット\",\n \"cancel\": \"キャンセル\",\n \"confirm\": \"確認\",\n \"delete\": \"削除\",\n \"edit\": \"編集\",\n \"create\": \"作成\",\n \"upload\": \"アップロード\",\n \"download\": \"ダウンロード\",\n \"refresh\": \"更新\",\n \"search\": \"検索\",\n \"enable\": \"有効化\",\n \"disable\": \"無効化\",\n \"enabled\": \"有効\",\n \"disabled\": \"無効\",\n \"preview\": \"プレビュー\",\n \"view\": \"閲覧\",\n \"content\": \"内容\",\n \"loading\": \"読み込み中...\",\n \"saving\": \"保存中...\",\n \"copy\": \"コピー\",\n \"copied\": \"クリップボードにコピーしました\",\n \"copyFailed\": \"クリップボードへのコピーに失敗しました\",\n \"contentPlaceholder\": \"内容を入力してください...\",\n \"help\": \"ヘルプ\",\n \"close\": \"閉じる\",\n \"actions\": \"操作\",\n \"total\": \"合計 {{count}} 件\",\n \"clear\": \"クリア\",\n \"paste\": \"貼り付け\"\n },\n \"harnesses\": {\n \"connected\": \"接続済み\",\n \"disconnected\": \"切断済み\",\n \"notConnected\": \"未接続\",\n \"comingSoon\": \"近日公開\",\n \"connect\": \"ChatGPTで接続\",\n \"disconnect\": \"切断\",\n \"apiKeyAuthenticated\": \"API キーで認証済み\",\n \"chatGptAuthenticated\": \"ChatGPT に接続済み\",\n \"cliAuthenticated\": \"{{type}} CLI 認証済み\"\n },\n \"desktop\": {\n \"closeWindow\": {\n \"title\": \"ウィンドウを閉じる\",\n \"description\": \"ウィンドウを閉じるときに実行する操作を選択してください。アプリを終了すると、実行中のすべてのタスクとスケジュール済みタスクが停止します。\",\n \"remember\": \"選択を記憶する\",\n \"showWindow\": \"ウィンドウを表示\",\n \"minimizeToTray\": \"トレイに最小化\",\n \"quitApp\": \"アプリを終了\",\n \"preference\": \"ウィンドウを閉じる\",\n \"askEveryTime\": \"毎回確認する\"\n }\n },\n \"chunkError\": {\n \"title\": \"ページの読み込みに失敗しました\",\n \"subTitle\": \"ネットワークの問題またはアプリの更新が原因の可能性があります。\",\n \"genericTitle\": \"問題が発生しました\",\n \"genericSubTitle\": \"このページのレンダリング中に予期しないエラーが発生しました。\",\n \"reload\": \"再読み込み\"\n },\n \"nav\": {\n \"chat\": \"チャット\",\n \"control\": \"コントロール\",\n \"channels\": \"チャンネル\",\n \"sessions\": \"セッション\",\n \"cronJobs\": \"定期実行\",\n \"heartbeat\": \"ハートビート\",\n \"agent\": \"ワークスペース\",\n \"workspace\": \"ファイル\",\n \"skills\": \"スキル\",\n \"skillPool\": \"スキルプール\",\n \"mcp\": \"MCP\",\n \"agentConfig\": \"設定\",\n \"settings\": \"設定\",\n \"models\": \"モデル\",\n \"environments\": \"環境変数\",\n \"security\": \"セキュリティ\",\n \"tokenUsage\": \"トークン使用量\",\n \"agentStats\": \"エージェント統計\",\n \"tools\": \"ツール\",\n \"voiceTranscription\": \"音声文字起こし\",\n \"agents\": \"エージェント管理\",\n \"debug\": \"デバッグ\",\n \"plugins\": \"プラグイン\",\n \"acp\": \"ACP\",\n \"backups\": \"バックアップ\",\n \"pluginManager\": \"プラグインマネージャー\"\n },\n \"debug\": {\n \"title\": \"デバッグ\",\n \"desc\": \"バックエンドのデーモンログを表示し、問題の調査に役立てます。このページを開いている間は自動で更新されます。\",\n \"level\": {\n \"all\": \"すべて\"\n },\n \"backend\": {\n \"title\": \"バックエンドログ\",\n \"autoRefresh\": \"自動更新\",\n \"newestFirst\": \"新しい順\",\n \"updatedAt\": \"更新時刻\",\n \"path\": \"ログファイル\",\n \"notFound\": \"バックエンドログファイルはまだ見つかっていません。\",\n \"placeholder\": \"バックエンドログがここに表示されます。\",\n \"loadFailed\": \"バックエンドログの読み込みに失敗しました\",\n \"searchPlaceholder\": \"バックエンドログを検索...\"\n },\n \"actions\": {\n \"refreshBackend\": \"バックエンドログを更新\",\n \"refreshSuccess\": \"ログを更新しました\",\n \"copyBackend\": \"バックエンドログをコピー\"\n }\n },\n \"agent\": {\n \"parent\": \"設定\",\n \"agents\": \"エージェント\",\n \"management\": \"エージェント管理\",\n \"pageDescription\": \"カスタムワークスペースとアイデンティティを持つ複数の AI エージェントを作成、設定、管理します。\",\n \"name\": \"名前\",\n \"id\": \"ID\",\n \"description\": \"説明\",\n \"workspace\": \"ワークスペースパス\",\n \"backend\": {\n \"column\": \"バックエンド\",\n \"eyebrow\": \"エージェントアーキテクチャ\",\n \"typeTitle\": \"このエージェントの動作方式を選択\",\n \"typeDescription\": \"エージェントの種類は、Chat、Coding、Channel のすべての会話に適用されます。\",\n \"nativeTitle\": \"QwenPaw ネイティブエージェント\",\n \"nativeBadge\": \"ネイティブ\",\n \"nativeDescription\": \"設定済みの LLM、Skills、ツール、メモリ、QwenPaw ランタイムを使用します。\",\n \"thirdPartyTitle\": \"サードパーティエージェント\",\n \"thirdPartyBadge\": \"サードパーティ\",\n \"thirdPartyDescription\": \"QwenPaw モデルの代わりに、認証済みの外部エージェントランタイムを使用します。\",\n \"providerTitle\": \"サードパーティエージェントを選択\",\n \"providerDescription\": \"認証情報はこのデバイスで共有されますが、プロジェクトと会話はエージェントごとに分離されます。\",\n \"codexHint\": \"ChatGPT OAuth · エージェントワークスペース\",\n \"qoderHint\": \"Qoder アカウントまたは PAT · エージェントワークスペース\",\n \"account\": \"Codex アカウント\",\n \"qoderAccount\": \"Qoder アカウント\",\n \"binary\": \"Codex 実行ファイル\",\n \"binaryHelp\": \"空欄の場合は Python SDK 付属の Codex CLI を優先し、単独インストールも自動検出します。手動で指定する場合は、macOS/Linux の codex または Windows の codex.exe のフルパスを入力してください。ChatGPT App やエディター拡張機能に内蔵されたバイナリはサポートされません。QwenPaw は CODEX_BINARY とバックエンドの PATH も参照します。\",\n \"binaryPlaceholder\": \"自動検出、または Codex 実行ファイルのパスを貼り付け\",\n \"qoderBinary\": \"Qoder 実行ファイル\",\n \"qoderBinaryHelp\": \"空欄の場合は Python SDK 同梱の qodercli を優先し、既存のインストールも自動検出します。手動指定では macOS/Linux の qodercli または Windows の qodercli.exe へのフルパスを入力してください。QODERCLI_PATH とバックエンドの PATH も参照します。\",\n \"qoderBinaryPlaceholder\": \"自動検出、または qodercli のパスを貼り付け\",\n \"detect\": \"再検出\",\n \"detectedBinary\": \"Codex を検出しました\",\n \"qoderDetectedBinary\": \"Qoder を検出しました\",\n \"apiKeyLoginHint\": \"ターミナルで codex login --with-api-key を実行して認証することもできます。\",\n \"qoderAuthHint\": \"接続すると Qoder のブラウザログインが開きます。qodercli login の実行、またはバックエンド起動前の QODER_PERSONAL_ACCESS_TOKEN 設定も利用できます。\",\n \"qoderConnect\": \"Qoder に接続\",\n \"model\": \"エージェントモデル\",\n \"modelDefault\": \"エージェントのデフォルトを使用\",\n \"defaultBadge\": \"デフォルト\",\n \"reasoningEffort\": \"推論レベル\",\n \"reasoningDefault\": \"モデルのデフォルトを使用\",\n \"codexNotFound\": \"Codex runtime が見つかりません。qwenpaw[codex] をインストールするか、単独の Codex CLI を指定してください。\",\n \"qoderNotFound\": \"Qoder runtime が見つかりません。qwenpaw[qoder] をインストールするか、qodercli を指定してください。\",\n \"chatSettingsHint\": \"Chat ツールバーでモデル、推論レベル、実行権限を選択します。\",\n \"capabilityTitle\": \"QwenPaw の機能\",\n \"inheritedSkills\": \"スキル\",\n \"inheritedMcp\": \"MCP サーバー\",\n \"runtimeInherited\": \"実行時に継承 · 新規セッション\",\n \"notSupported\": \"このプロバイダーでは未対応\",\n \"policyCompatibility\": \"MCP ポリシー\",\n \"toolAllowlistSupported\": \"ツール許可リスト対応\",\n \"policyLimited\": \"プロバイダーの制限あり\",\n \"chatModelHint\": \"エージェント単位の設定 · 次のターンから適用\",\n \"appliesNextTurn\": \"次のターンから適用\",\n \"approvalMode\": \"実行権限\",\n \"approvalPresets\": {\n \"ask\": {\n \"name\": \"変更前に確認\",\n \"description\": \"ワークスペースの変更を許可し、昇格操作の前に確認します。\"\n },\n \"read-only\": {\n \"name\": \"読み取り専用\",\n \"description\": \"ファイルを変更せずに確認します。\"\n },\n \"workspace\": {\n \"name\": \"ワークスペースアクセス\",\n \"description\": \"確認なしでワークスペースの変更を許可します。\"\n },\n \"accept-edits\": {\n \"name\": \"編集を許可\",\n \"description\": \"ファイル編集を許可し、その他の安全確認は維持します。\"\n },\n \"plan\": {\n \"name\": \"計画のみ\",\n \"description\": \"ファイルを変更せずに分析と計画を行います。\"\n },\n \"auto\": {\n \"name\": \"自動\",\n \"description\": \"安全な操作を Qoder が判断して実行します。\"\n },\n \"full-access\": {\n \"name\": \"フルアクセス\",\n \"description\": \"確認なしで無制限のローカル実行を許可します。\"\n }\n }\n },\n \"create\": \"エージェントを作成\",\n \"createTitle\": \"新しいエージェントを作成\",\n \"createSuccess\": \"エージェントが正常に作成されました\",\n \"createFailed\": \"エージェントの作成に失敗しました\",\n \"edit\": \"編集\",\n \"editTitle\": \"エージェントを編集 - {{name}}\",\n \"updateSuccess\": \"エージェントが正常に更新されました\",\n \"updateFailed\": \"エージェントの更新に失敗しました\",\n \"delete\": \"削除\",\n \"deleteConfirm\": \"エージェントの削除を確認\",\n \"deleteConfirmDesc\": \"削除後、エージェントは利用できなくなりますが、ワークスペースファイルは保持されます\",\n \"deleteSuccess\": \"エージェントが正常に削除されました\",\n \"deleteFailed\": \"エージェントの削除に失敗しました\",\n \"selectAgent\": \"エージェントを選択\",\n \"defaultDisplayName\": \"デフォルトエージェント\",\n \"currentWorkspace\": \"現在のエージェント\",\n \"switchSuccess\": \"エージェントが正常に切り替えられました\",\n \"switchFailed\": \"エージェントの切り替えに失敗しました\",\n \"loadFailed\": \"エージェントリストの読み込みに失敗しました\",\n \"loadConfigFailed\": \"エージェント設定の読み込みに失敗しました\",\n \"saveFailed\": \"エージェントの保存に失敗しました\",\n \"idRequired\": \"エージェント ID を入力してください\",\n \"idLabel\": \"エージェント ID(任意)\",\n \"idHelp\": \"空欄のままにすると自動生成されます。英数字、ハイフン、アンダースコアのみ使用可能。\",\n \"idPattern\": \"ID は英数字、アンダースコア、ハイフンのみ使用できます\",\n \"idPlaceholder\": \"例:my-agent\",\n \"nameRequired\": \"エージェント名を入力してください\",\n \"namePlaceholder\": \"例:マイエージェント\",\n \"descriptionPlaceholder\": \"このエージェントの目的を簡単に説明してください...\",\n \"workspaceHelp\": \"空欄のままにすると、~/.qwenpaw/workspaces/<id> に自動生成されます\",\n \"dragHandleTooltip\": \"ドラッグしてエージェントの順序を変更\",\n \"reorderSuccess\": \"エージェントの順序を保存しました\",\n \"reorderFailed\": \"エージェントの順序を保存できませんでした\",\n \"defaultNotEditable\": \"デフォルトのエージェントは編集できません\",\n \"defaultNotDeletable\": \"デフォルトのエージェントは削除できません\",\n \"disable\": \"無効化\",\n \"enable\": \"有効化\",\n \"disabled\": \"無効\",\n \"disableConfirm\": \"エージェントの無効化を確認\",\n \"disableConfirmDesc\": \"無効化後、エージェントインスタンスは起動しませんが、リストには表示されます\",\n \"enableConfirm\": \"エージェントの有効化を確認\",\n \"enableConfirmDesc\": \"有効化後、エージェントは切り替えて使用できるようになります\",\n \"disableSuccess\": \"エージェントが無効化されました\",\n \"enableSuccess\": \"エージェントが有効化されました\",\n \"toggleFailed\": \"エージェント状態の切り替えに失敗しました\",\n \"enableAgent\": \"{{name}} を有効化\",\n \"disableAgent\": \"{{name}} を無効化\",\n \"disabledAgents\": \"無効 ({{count}})\",\n \"pinnedAgents\": \"固定済み\",\n \"otherAgents\": \"その他のエージェント\",\n \"pinned\": \"固定済み\",\n \"defaultPinned\": \"デフォルトエージェントは常に固定されます\",\n \"longPressToPin\": \"長押しで固定\",\n \"longPressToUnpin\": \"長押しで固定解除\",\n \"pinAgent\": \"エージェントを固定\",\n \"unpinAgent\": \"エージェントの固定を解除\",\n \"pinSuccess\": \"エージェントを固定しました\",\n \"unpinSuccess\": \"エージェントの固定を解除しました\",\n \"pinFailed\": \"固定状態を更新できませんでした\",\n \"status\": {\n \"disabled\": \"無効\",\n \"pending\": \"起動待ち\",\n \"starting\": \"起動中\",\n \"running\": \"実行中\",\n \"failed\": \"起動失敗\",\n \"waitUntilStarted\": \"起動完了後に操作できます\"\n },\n \"defaultNotDisablable\": \"デフォルトのエージェントは無効化できません\",\n \"currentAgentDeleted\": \"現在のエージェントが削除されました。デフォルトエージェントに自動切り替えしました\",\n \"currentAgentDisabled\": \"現在のエージェントが無効化されました。デフォルトエージェントに自動切り替えしました\",\n \"switchedToDefault\": \"デフォルトエージェントに自動切り替えしました\",\n \"cannotSwitchToDisabled\": \"このエージェントは無効化されています。管理ページで有効化してください\",\n \"model\": \"モデル\",\n \"modelHelp\": \"このエージェントに特定の LLM モデルを割り当てます。空欄の場合はグローバルデフォルトが使用されます。\",\n \"modelPlaceholder\": \"グローバルデフォルト\",\n \"noConfiguredModels\": \"設定済みのモデルがありません\",\n \"modelColumn\": \"モデル\",\n \"initialSkills\": \"初期スキル\",\n \"initialSkillsHelp\": \"プールからスキルを追加選択します。後でいつでも追加・削除できます。\",\n \"selectAll\": \"すべて\",\n \"selectBuiltin\": \"組み込み\",\n \"selectNone\": \"クリア\",\n \"noPoolSkills\": \"プールに利用可能なスキルがありません\",\n \"addSkillsToAgent\": \"スキルを追加\",\n \"copyTooltip\": \"エージェント設定をコピー\",\n \"copyDefaultTooltip\": \"デフォルトエージェントをテンプレートとしてコピー\",\n \"copyTitle\": \"エージェント設定をコピー\",\n \"copyNameLabel\": \"新しいエージェント名\",\n \"copyOptionsLabel\": \"コピー内容\",\n \"copyOptionAgentJson\": \"agent.json(必須)\",\n \"copyOptionAgentJsonHint\": \"解析済みのソース設定を常にコピーし、ID・名前・workspace・channels のみリセットします。ディスク上の agent.json をそのまま複製するわけではありません。\",\n \"copyOptionMdFiles\": \"AGENTS / SOUL / PROFILE / HEARTBEAT / BOOTSTRAP.md\",\n \"copyOptionSkills\": \"skills/ + manifest\",\n \"copyOptionJobs\": \"jobs.json\",\n \"copyOptionJobsHint\": \"ジョブ定義のみをコピーし、実行履歴は含みません。作成後、新しいエージェントはスケジュールに従ってすぐに実行を開始します。\",\n \"copySuccess\": \"エージェント設定のコピーに成功しました\",\n \"copyFailed\": \"エージェント設定のコピーに失敗しました\"\n },\n \"checkpoints\": {\n \"nav\": \"チェックポイント\",\n \"title\": \"状態チェックポイント\",\n \"auto\": \"自動\",\n \"autoEnabled\": \"自動チェックポイントを有効にしました\",\n \"autoDisabled\": \"自動チェックポイントを無効にしました\",\n \"refresh\": \"更新\",\n \"snapshot\": \"スナップショット\",\n \"snapshotCreated\": \"スナップショットを作成しました\",\n \"graph\": \"グラフ\",\n \"checkpoint\": \"チェックポイント\",\n \"type\": \"種類\",\n \"commit\": \"コミット\",\n \"createdAt\": \"作成日時\",\n \"session\": \"セッション\",\n \"channel\": \"チャンネル\",\n \"parent\": \"親\",\n \"details\": \"チェックポイントの詳細\",\n \"search\": \"クエリ、名前、セッション、SHAを検索\",\n \"allTypes\": \"すべての種類\",\n \"allSessions\": \"すべてのセッション\",\n \"showingLatest\": \"最新{{count}}件を表示\",\n \"loadFailed\": \"チェックポイントを読み込めません\",\n \"retry\": \"再試行\",\n \"more\": \"その他の操作\",\n \"noMatches\": \"条件に一致するチェックポイントはありません。\",\n \"empty\": \"状態チェックポイントはまだありません。作成後、履歴がここに表示されます。\",\n \"kind\": {\n \"auto\": \"自動\",\n \"snapshot\": \"スナップショット\",\n \"safety\": \"安全\",\n \"commit\": \"コミット\"\n },\n \"summary\": { \"total\": \"件\" },\n \"snapshotDialog\": {\n \"title\": \"スナップショットを作成\",\n \"session\": \"セッション\",\n \"name\": \"名前\",\n \"placeholder\": \"任意の名前\"\n },\n \"gc\": {\n \"settingsAction\": \"自動整理設定\",\n \"settingsTitle\": \"自動整理設定\",\n \"keepCount\": \"保持する自動チェックポイント数\",\n \"keepDays\": \"自動チェックポイントの保持日数\",\n \"preRestoreDays\": \"復元前スナップショットの保持日数\",\n \"settingsSaved\": \"自動整理設定を保存しました\",\n \"thoroughAction\": \"Thoroughly compact checkpoints\",\n \"thoroughTitle\": \"Thoroughly compact checkpoints?\",\n \"thoroughDescription\": \"自動チェックポイントの保持ポリシーを無視して {{count}} 件を削除します。各セッションの HEAD は保持されます。\",\n \"thoroughConfirm\": \"Thoroughly compact\",\n \"thoroughSuccess\": \"Removed {{count}} checkpoint(s) during thorough compaction\",\n \"action\": \"チェックポイントを整理\",\n \"title\": \"保持ポリシーに従ってチェックポイントを整理しますか?\",\n \"description\": \"現在の保持ポリシーに従い、期限切れのチェックポイント {{count}} 件を削除します。各セッションの HEAD は保持されます。\",\n \"confirm\": \"整理\",\n \"success\": \"{{count}}件を削除しました\"\n },\n \"reset\": {\n \"action\": \"チェックポイントをリセット\",\n \"title\": \"すべてリセットしますか?\",\n \"description\": \"すべてのチェックポイントを完全に削除し、自動作成を無効にします。\",\n \"confirm\": \"リセット\",\n \"success\": \"リセットしました\"\n },\n \"restore\": {\n \"action\": \"復元\",\n \"title\": \"チェックポイントを復元\",\n \"preview\": \"復元をプレビュー\",\n \"confirm\": \"復元\",\n \"conversation\": \"会話\",\n \"memory\": \"メモリ\",\n \"files\": \"ワークスペースファイル\",\n \"refreshWarning\": \"会話が変更されます。復元後に会話を更新してください。\",\n \"selectedCount\": \"{{count}}ファイルを選択\",\n \"selectAll\": \"変更ファイルをすべて選択\",\n \"delete\": \"削除\",\n \"restore\": \"復元\",\n \"noFileChanges\": \"復元が必要なファイルはありません。\",\n \"success\": \"復元しました。会話を更新して状態を読み込んでください。\"\n }\n },\n \"workspace\": {\n \"title\": \"ファイル\",\n \"workspacePath\": \"ワークスペース:\",\n \"noFiles\": \"ファイルなし\",\n \"coreFiles\": \"コアファイル\",\n \"coreFilesDesc\": \"ペルソナ・アイデンティティ・ツールガイダンスを設定します。\",\n \"uploadTooltip\": \"ZIPファイルのみ\",\n \"uploadTooltipWithLimit\": \"ZIPファイルのみ、最大{{limit}}MB\",\n \"selectFile\": \"編集するファイルを選択\",\n \"fileContent\": \"ファイルの内容...\",\n \"uploadSuccess\": \"ファイルのアップロードに成功しました\",\n \"uploadFailed\": \"ファイルのアップロードに失敗しました\",\n \"downloadPreparing\": \"ワークスペースのダウンロードを準備中...\",\n \"downloadSuccess\": \"ワークスペースのダウンロードに成功しました\",\n \"downloadFailed\": \"ワークスペースのダウンロードに失敗しました\",\n \"zipOnly\": \"アップロードできるのは .zip ファイルのみです\",\n \"fileSizeExceeded\": \"ファイルサイズが{{limit}}MBの制限を超えています。現在のファイル: {{size}}MB\",\n \"systemPromptToggleTooltip\": \"システムプロンプトへのファイル読み込みを有効/無効にする\",\n \"memoryFileWarning\": \"MEMORY.md は通常、エージェントがツールを使用してオンデマンドで照会および読み込みます。システムプロンプトに読み込むと、コンテキストが長くなりすぎる可能性があります。\",\n \"configUpdated\": \"システムプロンプト設定が更新されました\",\n \"configUpdateFailed\": \"システムプロンプト設定の更新に失敗しました\",\n \"attribution\": \"ワークスペースの設計はOpenClawプロジェクトの一部を参考にしています — ありがとう!🐾\",\n \"saveSuccess\": \"保存しました\",\n \"saveFailed\": \"保存に失敗しました\"\n },\n \"skills\": {\n \"title\": \"スキル\",\n \"description\": \"エージェントのスキルと機能を管理します。\",\n \"qwenpawManaged\": \"QwenPaw 管理\",\n \"qwenpawManagedHint\": \"編集可能で、実行時に対応する外部エージェントへ反映されます。\",\n \"providerManaged\": \"サードパーティエージェントのスキル\",\n \"providerManagedHint\": \"現在のサードパーティエージェントから検出され、読み取り専用で表示されます。\",\n \"providerOnly\": \"現在のサードパーティエージェントのみ\",\n \"readOnly\": \"読み取り専用\",\n \"noDescription\": \"説明はありません。\",\n \"providerSkillDetails\": \"サードパーティエージェント Skill の詳細\",\n \"providerManagedDetailHint\": \"この Skill はサードパーティエージェントによって管理されています。QwenPaw では表示および使用できますが、変更はできません。\",\n \"provider\": \"サードパーティエージェント\",\n \"scope\": \"スコープ\",\n \"statusLabel\": \"ステータス\",\n \"descriptionLabel\": \"説明\",\n \"importHub\": \"URL からアップロード\",\n \"supportedSources\": \"対応ソース:\",\n \"importSkills\": \"スキルをインポート\",\n \"enterSkillUrl\": \"スキルのURLを入力\",\n \"supportedSkillUrlSources\": \"サポートされているスキルマーケットプレイス:\",\n \"urlExamples\": \"URLの例:\",\n \"urlValid\": \"{{source}} からの有効な URL\",\n \"invalidUrl\": \"無効な URL\",\n \"clickToFill\": \"クリックして URL を入力\",\n \"invalidSkillUrlSource\": \"サポートされているマーケットプレイスのスキルURLであることを確認してください\",\n \"type\": \"タイプ\",\n \"installedFrom\": \"インストール元\",\n \"source\": \"ソース\",\n \"path\": \"パス\",\n \"tags\": \"タグ\",\n \"builtin\": \"組み込み\",\n \"custom\": \"カスタム\",\n \"addSkill\": \"スキルを追加\",\n \"createSkill\": \"スキルを作成\",\n \"viewSkill\": \"スキルを表示\",\n \"editSkill\": \"スキルを編集\",\n \"skillName\": \"スキル名\",\n \"skillContent\": \"スキルの内容\",\n \"pleaseInputName\": \"スキル名を入力してください\",\n \"pleaseInputContent\": \"スキルの内容を入力してください\",\n \"skillNamePlaceholder\": \"例: weather_query\",\n \"contentPlaceholder\": \"---\\nname: <スキル名> (必須)\\ndescription: <スキルの説明> (必須)\\nmetadata: { \\\"qwenpaw\\\": { \\\"emoji\\\": \\\"🔧\\\" } }\\n---\\n\\nスキルの実装内容...\\n\\n# 例:\\n# ---\\n# name: cron\\n# description: qwenpawコマンド経由でcronジョブを管理 - タスクの作成・照会・一時停止・再開・削除\\n# metadata: { \\\"qwenpaw\\\": { \\\"emoji\\\": \\\"⏰\\\" } }\\n# ---\",\n \"createSuccess\": \"スキルを作成しました\",\n \"createFailed\": \"スキルの作成に失敗しました\",\n \"deleteConfirm\": \"このスキルを削除してもよろしいですか?\",\n \"deleteSuccess\": \"スキルを削除しました\",\n \"deleteFailed\": \"スキルの削除に失敗しました\",\n \"batchEnable\": \"有効化\",\n \"batchDisable\": \"無効化\",\n \"batchEnableSuccess\": \"{{count}}個のスキルを有効にしました\",\n \"batchEnablePartial\": \"{{enabled}}個有効化、{{failed}}個失敗\",\n \"batchEnableFailed\": \"一括有効化に失敗しました\",\n \"batchDisableSuccess\": \"{{count}}個のスキルを無効にしました\",\n \"batchDisablePartial\": \"{{disabled}}個無効化、{{failed}}個失敗\",\n \"batchDisableFailed\": \"一括無効化に失敗しました\",\n \"batchDeleteTitle\": \"{{count}}個のスキルを削除しますか?\",\n \"batchDeleteSuccess\": \"{{count}}個のスキルを削除しました\",\n \"batchDeletePartial\": \"{{deleted}}個削除、{{failed}}個失敗\",\n \"batchDeleteFailed\": \"一括削除に失敗しました\",\n \"selectedCount\": \"{{count}}個選択中\",\n \"selectAll\": \"すべて選択\",\n \"clearSelection\": \"クリア\",\n \"updateSuccess\": \"スキルを更新しました\",\n \"updateFailed\": \" スキルの更新に失敗しました\",\n \"frontmatterRequired\": \"スキルは --- で始まり --- で終わる必要があります\",\n \"frontmatterNameRequired\": \"スキルに必須フィールド「name」がありません\",\n \"frontmatterDescriptionRequired\": \"スキルに必須フィールド「description」がありません\",\n \"editNotSupported\": \"スキルの編集に失敗しました\",\n \"editNote\": \"ヒント: ここでスキルの内容、チャンネル範囲、設定を編集できます。\",\n \"create\": \"作成\",\n \"optimizeWithAI\": \"AI最適化\",\n \"stopOptimize\": \"停止\",\n \"optimizeSuccess\": \"スキルを最適化しました\",\n \"optimizeFailed\": \"スキルの最適化に失敗しました\",\n \"noContentToOptimize\": \"最適化するコンテンツがありません\",\n \"cancelImport\": \"インポートを中止\",\n \"importCancelled\": \"スキルのインポートを中止しました\",\n \"importTimeout\": \"スキルのインポートがタイムアウトしました。再試行するかネットワークを確認してください。\",\n \"uploadSkill\": \"スキルをアップロード\",\n \"uploadZip\": \"Zip をアップロード\",\n \"uploadSuccess\": \"スキルのアップロードに成功しました\",\n \"uploadFailed\": \"スキルのアップロードに失敗しました\",\n \"uploadNoChange\": \"新しいスキルはインポートされませんでした。既に存在する可能性があります\",\n \"zipOnly\": \"アップロードは .zip ファイルのみ対応しています\",\n \"fileSizeExceeded\": \"ファイルサイズが{{limit}}MBの制限を超えています。現在のファイル: {{size}}MB\",\n \"channels\": \"適用チャンネル\",\n \"allChannels\": \"すべて\",\n \"lastUpdated\": \"更新日時\",\n \"searchPlaceholder\": \"名前で絞り込み\",\n \"listView\": \"リスト表示\",\n \"gridView\": \"カード表示\",\n \"refreshHint\": \"ディスクからスキルを更新\",\n \"poolSync\": \"同期状態\",\n \"uploadedToPool\": \"スキルプールにアップロードしました\",\n \"downloadedToWorkspace\": \"現在のワークスペースにダウンロードしました\",\n \"builtinUpgradeTitle\": \"ビルトインスキルのアップグレード\",\n \"builtinUpgradeContent\": \"スキルプールにビルトインスキル「{{name}}」の新しいバージョンがあります。現在のバージョンを上書きしますか?\",\n \"languageSwitchTitle\": \"スキル言語の切り替え\",\n \"languageSwitchContent\": \"次のスキルはプール内に異なる言語バージョンがあり、上書きされます:\",\n \"overwriteExistingList\": \"次のスキルは既に存在しており、上書きされます:\",\n \"nameConflict\": \"名前の競合が検出されました。推奨される名前変更が入力されました。\",\n \"uploadToPoolHint\": \"選択したワークスペーススキルを共有ローカルスキルプールにアップロードします\",\n \"downloadFromPool\": \"スキルプールから読み込む\",\n \"uploadToPool\": \"スキルプールに同期\",\n \"emptyStateBadge\": \"新しいワークスペースは空で始まります\",\n \"emptyStateTitle\": \"このエージェントにはまだスキルがありません\",\n \"emptyStateText\": \"これは正常な状態です。新しいワークスペースにはスキルが自動的に含まれていません。スキルプールからスキルをダウンロードするか、ここで新しいスキルを作成して、このエージェントを装備してください。\",\n \"emptyStateDownload\": \"スキルプールから読み込む\",\n \"emptyStateCreate\": \"最初のスキルを作成\",\n \"selectWorkspaceSkill\": \"ワークスペーススキルを選択\",\n \"selectPoolItem\": \"スキルプール項目を選択\",\n \"renameOptional\": \"名前変更(任意)\",\n \"renamePlaceholder\": \"競合時に推奨される名前変更がここに表示されます\",\n \"renameSingleOnly\": \"名前変更はスキルを1つだけ選択した場合にのみ利用できます。\",\n \"downloadSkillPool\": \"スキルプールをダウンロード\",\n \"uploadSkillPool\": \"スキルプールにアップロード\",\n \"config\": \"設定\",\n \"configPlaceholder\": \"{\\\"KEY\\\": \\\"value\\\"}\",\n \"configInvalidJson\": \"無効な JSON 形式\",\n \"configSaved\": \"設定を保存しました\",\n \"loadFailed\": \"スキルの読み込みに失敗しました\",\n \"refreshFailed\": \"スキルの更新に失敗しました\",\n \"createdSuccessfully\": \"作成しました\",\n \"saveFailed\": \"保存に失敗しました\",\n \"importFailed\": \"インポートに失敗しました\",\n \"disabledSuccessfully\": \"無効化しました\",\n \"enabledSuccessfully\": \"有効化しました\",\n \"operationFailed\": \"操作に失敗しました\",\n \"provideUrl\": \"ハブスキルのURLを入力してください\",\n \"validUrl\": \"http:// または https:// で始まる有効なURLを入力してください\",\n \"importedSkill\": \"スキルをインポートしました: {{name}}\",\n \"batchOperation\": \"一括操作\",\n \"exitBatch\": \"一括操作を終了\",\n \"noSearchResults\": \"検索に一致するスキルが見つかりません\",\n \"filterByTag\": \"タグで絞り込み\",\n \"noTags\": \"利用可能なタグがありません\",\n \"enabledSkills\": \"有効なスキル\",\n \"disabledSkills\": \"無効なスキル\",\n \"active\": \"個有効\"\n },\n \"skillPool\": {\n \"description\": \"ビルトインスキルとワークスペースアップロード用の共有ローカルスキルプール。ClawHub や LobeHub などの外部インポートは別機能です。\",\n \"broadcast\": \"ブロードキャスト\",\n \"broadcastHint\": \"スキルプールのスキルを全てまたは選択したワークスペースにブロードキャスト\",\n \"refreshHint\": \"ディスクからスキルプールを更新\",\n \"deleteExternalConfirm\": \"外部スキルのファイル {{path}} をディスクから削除します。\",\n \"deleteExternalBatchWarning\": \"選択した外部スキルはディスクから完全に削除されます。\",\n \"broadcastTitle\": \"{{name}} をブロードキャスト\",\n \"importBuiltin\": \"ビルトインを取り込む\",\n \"importBuiltinHint\": \"ソースからビルトインスキルをプールに取り込みます\",\n \"importBuiltinAlertHint\": \"{{count}} 件のビルトイン変更を確認してください\",\n \"importBuiltinSuccess\": \"ビルトインを取り込みました: {{names}}\",\n \"importBuiltinConflicts\": \"名前の競合でスキップしました: {{names}}\",\n \"importBuiltinNoChanges\": \"取り込むビルトイン変更はありません\",\n \"importBuiltinFailed\": \"ビルトインの取り込みに失敗しました\",\n \"importBuiltinConflictTitle\": \"ビルトインの競合が見つかりました\",\n \"importBuiltinConflictContent\": \"以下のビルトインは現在のプール内コピーと異なります。確認するとソース版で上書きします。\",\n \"sourceVersion\": \"ソース版\",\n \"currentVersion\": \"現在の版\",\n \"builtinLanguage\": \"言語\",\n \"langDefault\": \"デフォルト\",\n \"langDefaultTooltip\": \"各スキルの元の言語設定を保持\",\n \"langEn\": \"英語\",\n \"langZh\": \"中国語\",\n \"builtinLanguageChangeTitle\": \"ビルトイン言語を切り替えますか?\",\n \"builtinLanguageChangeContent\": \"プール内の「{{name}}」を {{language}} 版のビルトインで上書きしますか?\",\n \"builtinLanguageChangeSuccess\": \"「{{name}}」を {{language}} に切り替えました\",\n \"builtinLanguageChangeFailed\": \"ビルトイン言語の切り替えに失敗しました\",\n \"importStatusMissing\": \"プールにありません\",\n \"importStatusCurrent\": \"最新です\",\n \"importStatusOutdated\": \"バージョンが古いです\",\n \"importStatusLanguageSwitchTo\": \"{{language}}版で上書きします\",\n \"importStatusConflict\": \"競合\",\n \"builtinNoticeSummary\": \"{{count}} 件のビルトイン変更が見つかりました\",\n \"builtinNoticeLineAdded\": \"追加: {{names}}\",\n \"builtinNoticeLineMissing\": \"不足: {{names}}\",\n \"builtinNoticeLineUpdated\": \"更新: {{names}}\",\n \"builtinNoticeLineRemoved\": \"削除: {{names}}\",\n \"zipImport\": \"ZIPインポート\",\n \"create\": \"作成\",\n \"edit\": \"編集\",\n \"delete\": \"削除\",\n \"descriptionLabel\": \"説明\",\n \"tags\": \"タグ\",\n \"tagsPlaceholder\": \"タグを選択または入力\",\n \"filterByTag\": \"タグで絞り込む\",\n \"version\": \"バージョン\",\n \"createTitle\": \"スキルプール項目を作成\",\n \"editTitle\": \"{{name}} を編集\",\n \"skillNamePlaceholder\": \"スキル名\",\n \"contentPlaceholder\": \"SKILL.md の内容\",\n \"nameConflict\": \"名前の競合が検出されました。推奨される名前変更が適用されました。\",\n \"deletedFromPool\": \"スキルプールから削除しました\",\n \"deleteBuiltinConfirm\": \"このビルトインスキルをプールから削除します。後でソースから再度取り込めます。\",\n \"deleteConfirm\": \"共有ローカルスキルプールからこのスキルを削除します。\",\n \"deleteTitle\": \"{{name}} を削除しますか?\",\n \"batchDeleteTitle\": \"{{count}}個のプールスキルを削除しますか?\",\n \"batchDeleteSuccess\": \"{{count}}個のプールスキルを削除しました\",\n \"batchDeletePartial\": \"{{deleted}}個削除、{{failed}}個失敗\",\n \"batchDeleteFailed\": \"一括削除に失敗しました\",\n \"imported\": \"インポート済み:{{names}}\",\n \"noNewImports\": \"新しいスキルはインポートされませんでした\",\n \"allBuiltin\": \"すべての組み込み\",\n \"selectWorkspaces\": \"ワークスペースにブロードキャスト\",\n \"allWorkspaces\": \"すべてのワークスペース\",\n \"optionalRename\": \"任意の名前変更\",\n \"back\": \"戻る\",\n \"skillCount\": \"{{count}} スキル\",\n \"skillName\": \"スキル名\",\n \"status\": \"状態\",\n \"installedFrom\": \"インストール元\",\n \"updateBuiltin\": \"更新\",\n \"updateConfirmTitle\": \"{{name}} を更新\",\n \"updateConfirmContent\": \"バージョン {{version}} に更新しますか?現在のプールコピーが最新の組み込みバージョンに置き換えられます。\",\n \"updateSuccess\": \"更新しました\",\n \"updateFailed\": \"更新に失敗しました\",\n \"broadcastSuccess\": \"ブロードキャストが完了しました\",\n \"broadcastFailed\": \"ブロードキャストに失敗しました\",\n \"overwriteConfirm\": \"既存のスキルを上書きしますか?\",\n \"overwriteTargetsContent\": \"次の既存ターゲットは上書きされます:\",\n \"builtinOverwriteTargetsContent\": \"次のビルトインターゲットはバージョンが異なるため、上書きされます:\",\n \"renameAndRetry\": \"同名のスキルが存在します。推奨名を使用しますか?\",\n \"multiConflictTitle\": \"複数の競合が検出されました\",\n \"multiConflictDesc\": \"以下のスキルは既に存在します。競合を個別に解決してから再試行してください:\",\n \"singleConflictRename\": \"\\\"{{name}}\\\" は既に存在します。\\\"{{suggested}}\\\" にリネームして続行しますか?\",\n \"statusUpToDate\": \"最新\",\n \"statusSynced\": \"同期済み\",\n \"statusOutdated\": \"古いです\",\n \"statusNotSynced\": \"未同期\",\n \"statusConflict\": \"競合\",\n \"renameEntry\": \"\\\"{{name}}\\\" の新しい名前:\",\n \"builtin\": \"組み込み\",\n \"custom\": \"カスタム\"\n },\n \"mcp\": {\n \"title\": \"MCPクライアント\",\n \"qwenpawManaged\": \"QwenPaw 管理\",\n \"qwenpawManagedHint\": \"統合コントロールプレーンで管理し、対応するサードパーティエージェントへ実行時に注入します。\",\n \"providerManaged\": \"{{provider}} ローカル MCP\",\n \"providerManagedHint\": \"サードパーティエージェント固有の設定から検出されます。読み取り専用で、そのエージェントだけで使用できます。\",\n \"providerOnly\": \"サードパーティエージェント限定\",\n \"readOnly\": \"読み取り専用\",\n \"create\": \"クライアントを作成\",\n \"formatSupport\": \"サポートされているフォーマット\",\n \"standardFormat\": \"標準(mcpServers でラップ)\",\n \"directFormat\": \"直接(キー → 設定オブジェクト)\",\n \"singleFormat\": \"単一クライアント(フラットなフィールド)\",\n \"emptyState\": \"MCPクライアントがまだ設定されていません\",\n \"loadError\": \"MCPクライアントの読み込みに失敗しました\",\n \"createSuccess\": \"MCPクライアントを作成しました\",\n \"createError\": \"MCPクライアントの作成に失敗しました\",\n \"updateSuccess\": \"MCPクライアントを更新しました\",\n \"updateError\": \"MCPクライアントの更新に失敗しました\",\n \"enableSuccess\": \"MCPクライアントを有効化しました\",\n \"disableSuccess\": \"MCPクライアントを無効化しました\",\n \"toggleError\": \"MCPクライアントのステータス変更に失敗しました\",\n \"deleteConfirm\": \"このMCPクライアントを削除してもよろしいですか?\",\n \"deleteSuccess\": \"MCPクライアントを削除しました\",\n \"deleteError\": \"MCPクライアントの削除に失敗しました\",\n \"maskedFieldHint\": \"機密フィールドは *** でマスクされています。マスク値の直接編集はできません。変更するには完全な新しい値に置き換えてください。\",\n \"tools\": \"ツール&権限\",\n \"noTools\": \"このMCPサーバーには利用可能なツールがありません\",\n \"toolsLoadError\": \"MCPサーバーからツール一覧の取得に失敗しました\",\n \"toolsConnecting\": \"MCPサーバーに接続中です。しばらくしてから再度お試しください\",\n \"toolSchema\": \"入力スキーマ\",\n \"access\": {\n \"title\": \"アクセス\",\n \"button\": \"アクセス\",\n \"summary\": \"MCPアクセスポリシー\",\n \"clientSection\": \"全体アクセス\",\n \"toolSection\": \"ツールアクセス\",\n \"default\": \"デフォルト\",\n \"sourceType\": \"ソース\",\n \"sourceValue\": \"ソース値\",\n \"sourceValueAllChannels\": \"すべてのチャンネル\",\n \"subjectType\": \"対象\",\n \"subjectValue\": \"対象値\",\n \"effectLabel\": \"ポリシー\",\n \"source\": {\n \"channel\": \"チャンネル\"\n },\n \"subjectTypeOption\": {\n \"all\": \"すべて\",\n \"user\": \"ユーザー\"\n },\n \"sourceValuePlaceholder\": {\n \"channel\": \"console / dingtalk\"\n },\n \"subjectValuePlaceholder\": \"例: alice\",\n \"subjectValuePlaceholderSelectedSource\": \"選択したソース内のユーザーID\",\n \"subjectValuePlaceholderScoped\": \"{{source}} のユーザーID\",\n \"subjectValueAll\": \"すべて\",\n \"recentUserPlaceholder\": \"ユーザーIDは選択したソース内で有効です。再入力または選択してください\",\n \"noRecentUsers\": \"このソースに最近のユーザーはいません。ユーザーIDを手入力できます\",\n \"ambiguousUserSourceWarning\": \"ユーザーIDはソース内でのみ一意です。チャンネルを指定して、別チャンネルのユーザーと誤って一致しないようにしてください。\",\n \"unknownUserValueWarning\": \"このユーザーIDは最近のアクティブユーザーにありません。保存は可能です。\",\n \"validation\": {\n \"missingUserValue\": \"具体的なソースを選択し、有効なユーザーIDを入力してください。\",\n \"ambiguousUserSource\": \"ユーザールールは、すべてのチャンネルではなく特定のチャンネルを対象にしてください。\",\n \"unknownUserValue\": \"このユーザーIDは最近のアクティブユーザーにありません。保存は可能です。\"\n },\n \"addRule\": \"ルールを追加\",\n \"deleteRule\": \"ルールを削除\",\n \"noClientRules\": \"ルールなし\",\n \"noRules\": \"ルールなし。デフォルトポリシーを使用します\",\n \"inherit\": \"継承({{effect}})\",\n \"stale\": \"見つかりません\",\n \"disabledTools\": \"このMCPクライアントは無効です。保存済みのアクセスルールは引き続き編集できます。\",\n \"discardTitle\": \"未保存の変更を破棄しますか?\",\n \"discardContent\": \"未保存のアクセスポリシー変更は失われます。\",\n \"loadError\": \"MCPアクセスポリシーの読み込みに失敗しました\",\n \"saveSuccess\": \"MCPアクセスポリシーを保存しました\",\n \"saveError\": \"MCPアクセスポリシーの保存に失敗しました\",\n \"unmanagedRules\": \"{{count}} 件の詳細ルール\",\n \"effect\": {\n \"allow\": \"許可\",\n \"ask\": \"確認\",\n \"deny\": \"拒否\"\n }\n },\n \"tab\": {\n \"json\": \"JSONインポート\",\n \"form\": \"フォームモード\"\n },\n \"form\": {\n \"key\": \"クライアントキー\",\n \"keyPlaceholder\": \"my-mcp-server\",\n \"keyRequired\": \"クライアントキーは必須です\",\n \"name\": \"表示名\",\n \"namePlaceholder\": \"My MCP Server\",\n \"nameRequired\": \"表示名は必須です\",\n \"transport\": \"トランスポート\",\n \"url\": \"サーバーURL\",\n \"urlRequired\": \"リモートMCPサーバーにはURLが必要です\",\n \"command\": \"コマンド\",\n \"commandRequired\": \"stdioトランスポートにはコマンドが必要です\",\n \"args\": \"引数(スペースまたは改行区切り)\",\n \"description\": \"説明\",\n \"descriptionPlaceholder\": \"任意の説明\",\n \"env\": \"環境変数\",\n \"envPlaceholder\": \"KEY=VALUE(1行に1つ)\"\n },\n \"oauth\": {\n \"enableOAuth\": \"OAuth 認証を使用する\",\n \"manage\": \"OAuth\",\n \"authorized\": \"認証済み\",\n \"notAuthorized\": \"未認証\",\n \"expired\": \"トークン期限切れ\",\n \"failed\": \"認証失敗\",\n \"waiting\": \"認証待ち...\",\n \"authorize\": \"認証する\",\n \"reauthorize\": \"再認証する\",\n \"revoke\": \"取り消す\",\n \"startFailed\": \"OAuthフローの開始に失敗しました\",\n \"authFailed\": \"OAuth認証に失敗しました\",\n \"popupBlocked\": \"ポップアップがブラウザにブロックされました。許可してから再試行してください。\",\n \"windowClosed\": \"認証ウィンドウが閉じられました。再試行してください。\",\n \"noUrl\": \"先にサーバーURLを入力してください\",\n \"noClientKey\": \"先にクライアントを作成してください\",\n \"showAdvanced\": \"詳細オプション\",\n \"hideAdvanced\": \"詳細オプションを隠す\",\n \"clientId\": \"Client ID(任意)\",\n \"clientIdPlaceholder\": \"空白のままで動的クライアント登録を使用\",\n \"scope\": \"Scope(任意)\",\n \"scopePlaceholder\": \"openid profile email\",\n \"authEndpoint\": \"Authorization Endpoint(上書き)\",\n \"tokenEndpoint\": \"Token Endpoint(上書き)\",\n \"endpointHint\": \"空白のままにするとMCPサーバーから自動検出されます。\"\n }\n },\n \"heartbeat\": {\n \"title\": \"ハートビート\",\n \"description\": \"一定間隔でHEARTBEAT.mdを実行してセルフチェックを行います。デフォルトでは現在の会話に影響を与えずサイレントに動作しますが、オプションで最後のチャットチャンネルに返信を送ることもできます。\",\n \"enabled\": \"ハートビートを有効化\",\n \"every\": \"間隔\",\n \"everyRequired\": \"必須\",\n \"everyMin\": \"1以上の値が必要です\",\n \"timeoutSeconds\": \"実行タイムアウト(秒)\",\n \"timeoutRequired\": \"必須\",\n \"timeoutMin\": \"1以上の値が必要です\",\n \"timeoutMax\": \"3600以下の値が必要です\",\n \"unitMinutes\": \"分\",\n \"unitHours\": \"時間\",\n \"target\": \"返信先\",\n \"targetMain\": \"サイレントモード(デフォルト、チャンネル出力なし)\",\n \"targetLast\": \"最後のチャットチャンネルに送信\",\n \"activeHours\": \"稼働時間(任意)\",\n \"activeStart\": \"開始時刻\",\n \"activeEnd\": \"終了時刻\",\n \"loadFailed\": \"ハートビート設定の読み込みに失敗しました\",\n \"saveSuccess\": \"保存しました。ハートビートをホットリロードしました\",\n \"saveFailed\": \"ハートビート設定の保存に失敗しました\",\n \"targetInbox\": \"受信トレイに送信\",\n \"runNow\": \"今すぐ実行\",\n \"runNowSuccess\": \"ハートビートを実行しました。受信トレイまたは送信先チャンネルを確認してください。\",\n \"runNowFailed\": \"ハートビートの実行に失敗しました\"\n },\n \"cronJobs\": {\n \"title\": \"定期実行\",\n \"description\": \"指定した時刻に自動実行されるスケジュールタスクを作成・管理します。\",\n \"createJob\": \"ジョブを作成\",\n \"editJob\": \"ジョブを編集\",\n \"confirmDelete\": \"削除の確認\",\n \"deleteConfirm\": \"この定期実行ジョブを削除してもよろしいですか?\",\n \"okText\": \"OK\",\n \"cancelText\": \"キャンセル\",\n \"deleteText\": \"削除\",\n \"id\": \"ジョブID\",\n \"name\": \"ジョブ名\",\n \"enabled\": \"ステータス\",\n \"scheduleType\": \"スケジュールタイプ\",\n \"scheduleCron\": \"スケジュール(Cron)\",\n \"scheduleCronLabel\": \"スケジュール(Cron)\",\n \"scheduleTimezone\": \"タイムゾーン\",\n \"cronType\": \"頻度\",\n \"cronTypeHourly\": \"毎時\",\n \"cronTypeDaily\": \"毎日\",\n \"cronTypeWeekly\": \"毎週\",\n \"cronTypeCustom\": \"カスタム\",\n \"cronTime\": \"時刻\",\n \"cronDaysOfWeek\": \"曜日\",\n \"cronDayMon\": \"月曜日\",\n \"cronDayTue\": \"火曜日\",\n \"cronDayWed\": \"水曜日\",\n \"cronDayThu\": \"木曜日\",\n \"cronDayFri\": \"金曜日\",\n \"cronDaySat\": \"土曜日\",\n \"cronDaySun\": \"日曜日\",\n \"cronCustomExpression\": \"Cron式\",\n \"taskText\": \"メッセージ内容\",\n \"action\": \"操作\",\n \"disable\": \"無効化\",\n \"executeNow\": \"今すぐ実行\",\n \"edit\": \"編集\",\n \"delete\": \"削除\",\n \"totalItems\": \"合計 {{count}} 件\",\n \"perPage\": \"件/ページ\",\n \"pleaseInputId\": \"ジョブIDを入力してください\",\n \"pleaseInputName\": \"ジョブ名を入力してください\",\n \"pleaseInputCron\": \"Cron式を入力してください\",\n \"pleaseSelectTaskType\": \"タスクタイプを選択してください\",\n \"pleaseInputRequest\": \"リクエスト内容を入力してください\",\n \"pleaseInputMessageContent\": \"メッセージ内容を入力してください\",\n \"pleaseInputChannel\": \"対象チャンネルを入力してください\",\n \"pleaseInputUserId\": \"対象ユーザーIDを入力してください\",\n \"pleaseInputSessionId\": \"対象セッションIDを入力してください\",\n \"jobIdPlaceholder\": \"例: daily-report-job\",\n \"jobNamePlaceholder\": \"例: 毎朝の日報\",\n \"selectTimezone\": \"タイムゾーンを選択\",\n \"taskDescriptionPlaceholder\": \"テキストタスクの場合は送信する本文を入力...\",\n \"invalidJsonFormat\": \"JSONフォーマットが不正です\",\n \"jsonFormatRequired\": \"JSONフォーマットが必要です\",\n \"taskType\": \"タスクタイプ\",\n \"text\": \"メッセージ内容\",\n \"requestInput\": \"リクエスト内容\",\n \"requestSessionId\": \"リクエストセッションID\",\n \"requestUserId\": \"リクエストユーザーID\",\n \"dispatchChannel\": \"対象チャンネル\",\n \"dispatchTargetUserId\": \"対象ユーザーID\",\n \"dispatchTargetSessionId\": \"対象セッションID\",\n \"dispatchMode\": \"配信モード\",\n \"silentDelivery\": \"サイレント配信\",\n \"runtimeShareSession\": \"セッション共有\",\n \"runtimeMaxConcurrency\": \"最大同時実行数\",\n \"runtimeTimeoutSeconds\": \"タイムアウト(秒)\",\n \"runtimeMisfireGraceSeconds\": \"遅延許容時間(秒)\",\n \"runtimeToolSafety\": \"ツール実行安全チェック\",\n \"idTooltip\": \"タスクの作成時にシステムによって生成され、編集不可の一意のUUID。\",\n \"nameTooltip\": \"このジョブを識別するためのわかりやすい名前。\",\n \"cronTooltip\": \"タスクの実行タイミングを定義します\",\n \"cronExample\": \"よく使う例: '0 9 * * *' = 毎日9時 | '*/30 * * * *' = 30分ごと | '0 */2 * * *' = 2時間ごと | '0 0 * * 0' = 日曜0時\",\n \"cronHelper\": \"Cron式に慣れていませんか?\",\n \"cronHelperLink\": \"オンラインジェネレーターを使う\",\n \"timezoneTooltip\": \"Cronスケジュールのタイムゾーン。デフォルト: UTC\",\n \"taskTypeTooltip\": \"シンプルなメッセージタスクには「テキスト」を、複雑なエージェントワークフローには「エージェント」を選択してください。\",\n \"textTooltip\": \"シンプルなメッセージタスク:実際に送る本文です。タスクタイプが「text」のとき必須です。\",\n \"requestInputTooltip\": \"JSON形式のメッセージ内容。エージェントが受け取って処理する内容です。タスクタイプが「agent」のとき必須です。\",\n \"requestInputExample\": \"フォーマット: [{\\\"role\\\":\\\"user\\\",\\\"content\\\":[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"ここにメッセージを入力\\\"}]}]\",\n \"requestSessionIdTooltip\": \"リクエストコンテキストのセッションID。不明な場合は「default」を使用してください。\",\n \"requestUserIdTooltip\": \"リクエストを開始するユーザーID。自動タスクには「system」を使用してください。\",\n \"dispatchChannelTooltip\": \"レスポンスを送信する対象チャンネル(例: 'console', 'discord', 'imessage')。\",\n \"dispatchTargetUserIdTooltip\": \"対象チャンネルでレスポンスを受け取るユーザーID。\",\n \"dispatchTargetSessionIdTooltip\": \"対象チャンネルでレスポンスを届けるセッションID。\",\n \"dispatchModeTooltip\": \"リアルタイム応答には「ストリーム」を、完了後の応答には「ファイナル」を選択してください。\",\n \"silentDeliveryTooltip\": \"エージェントタスクを実行してセッションとトレースを保持しますが、結果はチャンネルに送信しません。\",\n \"shareSessionTooltip\": \"有効にすると、対象ユーザーとセッションを共有します。無効にすると、実行ごとに一意のIDで独立したコンテキストを作成します。履歴を必要としない独立したタスクに適しています。デフォルト: 有効\",\n \"maxConcurrencyTooltip\": \"このジョブの最大同時実行数。デフォルト: 1\",\n \"timeoutSecondsTooltip\": \"最大実行時間(秒)。超過するとジョブが終了します。\",\n \"misfireGraceSecondsTooltip\": \"実行漏れの許容時間。スケジュール時刻をこの時間以上過ぎた場合は実行されません。\",\n \"toolSafetyTooltip\": \"有効にすると、リスクの高いツール呼び出しにユーザー承認が必要です(無人のcronジョブをブロックする可能性があります)。無効にすると、すべてのツールが承認なしで実行されます。デフォルト:無効\",\n \"executeNowTitle\": \"タスクを今すぐ実行\",\n \"executeNowContent\": \"\\\"{{name}}\\\" を今すぐ実行してもよろしいですか?\",\n \"executeNowConfirm\": \"今すぐ実行\",\n \"createFromTemplate\": \"テンプレートから作成\",\n \"templateModalTitle\": \"タスクテンプレートを選択\",\n \"templateModalDescription\": \"内蔵テンプレートを選び、保存前に主要パラメータを調整します。\",\n \"useTemplate\": \"このテンプレートを使用\",\n \"templateTagPersonal\": \"個人\",\n \"templateTagTeam\": \"チーム\",\n \"templateTagReminder\": \"リマインダー\",\n \"templateTagCalendar\": \"カレンダー\",\n \"templateTagCalendarRecommended\": \"カレンダー推奨\",\n \"saveResultToInbox\": \"実行結果を受信トレイに保存\",\n \"saveResultToInboxTooltip\": \"有効時、実行と配信が成功した結果を受信トレイに保存します。配信失敗時はフォールバックとして必ず受信トレイに記録されます。\",\n \"scheduleFilterAll\": \"すべてのスケジュール種別\",\n \"scheduleTypeRecurring\": \"繰り返し\",\n \"scheduleTypeOnce\": \"日程\",\n \"onceRunAt\": \"実行日時\",\n \"repeatEnabled\": \"繰り返し実行\",\n \"repeatEnabledTooltip\": \"この開始時刻から定期実行を有効化します\",\n \"repeatFrequency\": \"繰り返し頻度\",\n \"repeatEveryDays\": \"繰り返し間隔(日)\",\n \"repeatEveryPrefix\": \"毎\",\n \"repeatEverySuffix\": \"日\",\n \"repeatEndType\": \"終了条件\",\n \"repeatEndNever\": \"終了しない\",\n \"repeatEndUntil\": \"日時で終了\",\n \"repeatEndCount\": \"回数で終了\",\n \"repeatUntil\": \"終了日時\",\n \"repeatCount\": \"実行回数\",\n \"pleaseSelectScheduleType\": \"スケジュール種別を選択してください\",\n \"pleaseInputRunAt\": \"実行日時を選択してください\",\n \"pleaseInputRepeatEveryDays\": \"繰り返し間隔(日)を入力してください\",\n \"pleaseSelectRepeatEndType\": \"終了条件を選択してください\",\n \"pleaseInputRepeatUntil\": \"終了日時を選択してください\",\n \"pleaseInputRepeatCount\": \"実行回数を入力してください\",\n \"validation\": {\n \"cronRequired\": \"繰り返しスケジュールには Cron 式が必要です。\",\n \"runAtRequired\": \"単発スケジュールには実行日時が必要です。\",\n \"repeatUntilRequired\": \"「日時で終了」を選択した場合は終了日時が必要です。\",\n \"repeatCountRequired\": \"「回数で終了」を選択した場合は実行回数が必要です。\",\n \"repeatUntilAfterRunAt\": \"終了日時は実行日時より後である必要があります。実行日時より後の日時を指定してください。\",\n \"textRequired\": \"タスクタイプが text の場合、メッセージ内容は必須です。\",\n \"requestRequired\": \"タスクタイプが agent の場合、リクエスト内容は必須です。\",\n \"invalidCronExpression\": \"Cron 式が不正です。5 フィールド形式を使用してください。\"\n },\n \"listView\": \"リスト表示\",\n \"calendarView\": \"カレンダー表示\",\n \"calendarEmptyHint\": \"Scheduled タスクはまだありません。作成するとカレンダーに表示されます。\",\n \"calendarMoreItems\": \"他 {{count}} 件\",\n \"calendarDayListTitle\": \"{{date}} の Scheduled タスク\",\n \"executionHistory\": \"実行履歴\",\n \"historyTitle\": \"実行履歴 - {{name}}\",\n \"historyEmpty\": \"実行履歴はまだありません。\",\n \"historyStatusSuccess\": \"成功\",\n \"historyStatusFailed\": \"失敗\",\n \"historyStatusRunning\": \"実行中\",\n \"historyStatusCancelled\": \"キャンセル\",\n \"historyTriggerScheduled\": \"スケジュール実行\",\n \"historyTriggerManual\": \"手動実行\",\n \"historyExpand\": \"展開\",\n \"historyCollapse\": \"折りたたむ\",\n \"templates\": {\n \"dailyTechNewsBrief\": {\n \"title\": \"平日テックニュース要約\",\n \"description\": \"平日 09:30 に注目の技術ニュースを短く要約して配信します。\",\n \"frequency\": \"平日 09:30 (30 9 * * 1-5)\"\n },\n \"dailyEveningReflection\": {\n \"title\": \"毎日夜の振り返り\",\n \"description\": \"平日 20:00 に当日の振り返りを促します。\",\n \"frequency\": \"平日 20:00 (0 20 * * 1-5)\"\n },\n \"weekendRelaxationReminder\": {\n \"title\": \"週末リラックス通知\",\n \"description\": \"週末朝におすすめ映画を配信します。\",\n \"frequency\": \"週末 10:00 (0 10 * * 6,0)\"\n },\n \"pomodoroBreakReminder\": {\n \"title\": \"ポモドーロ休憩リマインダー\",\n \"description\": \"業務時間中に 25 分ごとに休憩と水分補給を促します。\",\n \"frequency\": \"平日 09:00-17:59、25 分ごと (*/25 9-17 * * 1-5)\"\n },\n \"petCareReminder\": {\n \"title\": \"ペットケアリマインダー\",\n \"description\": \"毎月15日夜に駆虫・ワクチン確認を通知します。\",\n \"frequency\": \"毎月15日 20:00 (0 20 15 * *)\"\n },\n \"onceTextBirthdayReminder\": {\n \"title\": \"誕生日リマインダー\",\n \"description\": \"xx が 1月1日に誕生日であることを通知します。\",\n \"frequency\": \"実行時刻:2026-01-01 09:00\"\n },\n \"onceAgentBusinessTripPrep\": {\n \"title\": \"出張前の準備\",\n \"description\": \"目的地の天気を確認し、出張準備の提案を行います。\",\n \"frequency\": \"実行時刻:2026-01-01 20:00\"\n },\n \"repeatCountTextMedicineReminder\": {\n \"title\": \"服薬リマインダー(14回)\",\n \"description\": \"14回の療程で、時間どおりの服薬を通知します。\",\n \"frequency\": \"初回:2026-01-01 09:00|1日ごと|14回\"\n },\n \"repeatCountAgentDietPlan\": {\n \"title\": \"14日間の食事プラン\",\n \"description\": \"14日間、毎日の食事提案を生成して送信します。\",\n \"frequency\": \"初回:2026-01-01 08:00|1日ごと|14回\"\n },\n \"repeatUntilTextWeeklyMeeting\": {\n \"title\": \"2か月間の週次会議リマインダー\",\n \"description\": \"設定した締切まで毎週、会議参加を通知します。\",\n \"frequency\": \"初回:2026-01-02 08:45|7日ごと|2026-03-01まで\"\n },\n \"repeatUntilAgentWeeklySummary\": {\n \"title\": \"会議前の週次サマリー\",\n \"description\": \"毎週会議前に、直近1週間の memory をもとに業務サマリーを生成します。\",\n \"frequency\": \"初回:2026-01-02 08:30|7日ごと|2026-03-01まで\"\n }\n }\n },\n \"channels\": {\n \"title\": \"チャンネル\",\n \"description\": \"メッセージチャンネルの管理と設定\",\n \"loading\": \"チャンネルを読み込み中...\",\n \"configSaved\": \"設定を保存しました\",\n \"configFailed\": \"設定の保存に失敗しました\",\n \"channelType\": \"チャンネルタイプ\",\n \"status\": \"ステータス\",\n \"totalItems\": \"合計 {{count}} 件\",\n \"botPrefix\": \"Botプレフィックス\",\n \"showToolCalls\": \"ツール呼び出し情報を表示\",\n \"showToolCallsTooltip\": \"ツール呼び出し情報をユーザーに表示します(オフにすると非表示)\",\n \"showToolResults\": \"ツール結果情報を表示\",\n \"showToolResultsTooltip\": \"ツール結果情報をユーザーに表示します(オフにすると非表示)\",\n \"toolCallMaxLength\": \"ツール呼び出しの最大表示長\",\n \"toolResultMaxLength\": \"ツール結果の最大表示長\",\n \"toolMaxLengthTooltip\": \"0 に設定すると省略しません\",\n \"showThinking\": \"思考過程を表示\",\n \"showThinkingTooltip\": \"モデルの思考・推論内容をユーザーに表示します(オフにすると非表示)\",\n \"noTextDebounce\": \"テキストなしデバウンス\",\n \"noTextDebounceTooltip\": \"有効にすると、メディアのみのメッセージ(画像/動画/ファイル)は一時保存され、後続のテキストメッセージ到着時に結合されます。無効にするとすべてのメッセージが即時処理されます。音声メッセージは常に即時処理されます。\",\n \"streamingEnabled\": \"ストリーミング出力\",\n \"streamingEnabledDingtalkHint\": \"メッセージタイプがCardの場合のみ有効\",\n \"streamingEnabledFeishuHint\": \"Feishuオープンプラットフォームの権限管理ページでcardkit:card:write権限を有効にする必要があります\",\n \"ackMessage\": \"即時確認メッセージ\",\n \"ackMessageTooltip\": \"メッセージ受信時にエージェント処理の前に即時返信を送信します。空欄で無効化。\",\n \"ackMessagePlaceholder\": \"⏳ 処理中...\",\n \"notSet\": \"未設定\",\n \"clickCardToEdit\": \"カードをクリックして編集\",\n \"settings\": \"設定\",\n \"channelSettings\": \"チャンネル設定\",\n \"pleaseInputDbPath\": \"DBパスを入力してください\",\n \"pleaseInputPollInterval\": \"ポーリング間隔を入力してください\",\n \"discordBotToken\": \"Discord Botトークン\",\n \"acceptBotMessages\": \"Botメッセージを受信\",\n \"acceptBotMessagesTooltip\": \"有効にすると、他のBotからのメッセージを受信します\",\n \"httpProxyPlaceholder\": \"http://127.0.0.1:18118\",\n \"httpProxyAuthPlaceholder\": \"ユーザー:パスワード\",\n \"dbPathPlaceholder\": \"~/Library/Messages/chat.db\",\n \"botPrefixPlaceholder\": \"@bot\",\n \"dmPolicy\": \"DMポリシー\",\n \"dmPolicyTooltip\": \"ダイレクトメッセージでBotと対話できるユーザーを制御します。オープン: 全員、許可リスト: 認証済みユーザーのみ\",\n \"groupPolicy\": \"グループポリシー\",\n \"groupPolicyTooltip\": \"グループチャットでBotと対話できるユーザーを制御します。オープン: 全員、許可リスト: 認証済みユーザーのみ\",\n \"requireMention\": \"@メンション必須\",\n \"requireMentionTooltip\": \"有効にすると、グループチャットでは明示的に@メンションされた場合のみ応答します\",\n \"policyOpen\": \"オープン\",\n \"policyAllowlist\": \"許可リスト\",\n \"allowFrom\": \"許可ユーザー\",\n \"allowFromTooltip\": \"Botと対話できるユーザーIDのリスト(フォーマット: ニックネーム#末尾4桁、例: John#1234)\",\n \"allowFromPlaceholder\": \"ユーザーIDを入力してEnterで追加\",\n \"denyMessage\": \"アクセス拒否メッセージ\",\n \"denyMessageTooltip\": \"アクセスが拒否されたユーザーに表示されるカスタムメッセージ\",\n \"twilioAccountSid\": \"Twilio アカウント SID\",\n \"twilioAuthToken\": \"Twilio 認証トークン\",\n \"phoneNumber\": \"電話番号\",\n \"phoneNumberSid\": \"電話番号 SID\",\n \"phoneNumberSidHelp\": \"TwilioコンソールのPhone Numbers → Active Numbersで確認できます。\",\n \"ttsProvider\": \"TTSプロバイダー\",\n \"ttsVoice\": \"TTSボイス\",\n \"sttProvider\": \"STTプロバイダー\",\n \"language\": \"言語\",\n \"welcomeGreeting\": \"ウェルカムメッセージ\",\n \"voiceSetupGuide\": \"Twilioアカウントを作成して電話番号を取得し、以下に認証情報を入力してください。アカウント SIDと認証トークンはTwilioコンソールのダッシュボードで確認できます。電話番号 SIDはPhone Numbers → Active Numbersに記載されています。\",\n \"voiceSetupLink\": \"Twilioコンソールを開く\",\n \"sipSetupGuide\": \"SIPレジストラ(Asterisk、FreeSWITCHなど)を設定し、以下にSIP認証情報を入力してください。DevモードではpyVoIPがローカルでSIP/RTPを処理します。ProductionモードではLiveKit SIP Serverを使用します。\",\n \"sipMode\": \"SIPモード\",\n \"sipModeTooltip\": \"Dev:ローカルテスト用のPython pyVoIP。Production:高並行性のLiveKit SIP Server。\",\n \"sipServer\": \"SIPサーバー\",\n \"sipServerPlaceholder\": \"空欄で内蔵レジストラを使用\",\n \"sipServerPlaceholderLivekit\": \"LiveKitモードでは不要\",\n \"sipUsername\": \"SIPユーザー名\",\n \"sipPassword\": \"SIPパスワード\",\n \"sipPort\": \"SIPポート\",\n \"sipTransport\": \"トランスポート\",\n \"sipDashscopeApiKey\": \"DashScope API Key\",\n \"sipDashscopeApiKeyTooltip\": \"Aliyun DashScope STT/TTS用のAPIキー。空の場合、DASHSCOPE_API_KEY環境変数にフォールバックします。\",\n \"livekitUrl\": \"LiveKit URL\",\n \"livekitApiKey\": \"LiveKit APIキー\",\n \"livekitApiSecret\": \"LiveKit APIシークレット\",\n \"livekitSipTrunkId\": \"LiveKit SIPトランクID\",\n \"livekitRoomName\": \"LiveKit ルーム名\",\n \"livekitRoomNameTooltip\": \"SIP着信を待機するLiveKitルーム名。SIP Dispatch Ruleのルーム名と一致させてください。\",\n \"loginWeCom\": \"WeCom QRコードを取得\",\n \"wecomSetupGuide\": \"QRコードでボットを作成する際、ボットの権限を承認する画面では「承認をスキップ」を選択してください。「承認を確認」を選択すると、ボット作成者以外のユーザーがボットと対話できなくなる場合があります。\",\n \"wecomScanAuth\": \"QRコードで認証\",\n \"wecomAuthHint\": \"上のQRコードをWeComでスキャンして認証してください。認証後、Bot IDとSecretが自動入力されます。\",\n \"wecomAuthSuccess\": \"WeComボットの認証に成功しました。Bot IDとSecretが自動入力されました。\",\n \"wecomQrcodeFailed\": \"WeCom QRコードの取得に失敗しました。もう一度お試しください。\",\n \"slackBotToken\": \"Bot Token\",\n \"slackBotTokenTooltip\": \"Slack Bot User OAuth Token(xoxb- で始まる)\",\n \"slackAppToken\": \"App Token\",\n \"slackAppTokenTooltip\": \"Slack App-Level Token(Socket Mode用、xapp- で始まる)\",\n \"slackProxyTooltip\": \"Slack API接続用HTTPプロキシURL\",\n \"dingtalkSetupGuide\": \"DingTalkでQRコードをスキャンしてボットを作成します。スキャン後、DingTalkアプリが自動作成され、Client IDとClient Secretが自動入力されます。\",\n \"dingtalkScanAuth\": \"スキャンしてボットを作成\",\n \"dingtalkGetQrcode\": \"DingTalk QRコードを取得\",\n \"dingtalkScanHint\": \"上のQRコードをDingTalkでスキャンしてください。認証後、Client IDとClient Secretが自動入力されます。\",\n \"dingtalkAuthSuccess\": \"DingTalkボットが作成されました。Client IDとClient Secretが自動入力されました。\",\n \"dingtalkQrcodeFailed\": \"DingTalk QRコードの取得に失敗しました。もう一度お試しください。\",\n \"dingtalkQrcodeExpired\": \"QRコードの有効期限が切れました。再取得してください。\",\n \"qqSetupGuide\": \"QQを使用してQRコードをスキャンし、ボットの認証を行ってください。スキャン後、APP IDとClient Secretが自動的に取得され、入力欄に反映されます。\",\n \"qqScanAuth\": \"QRコードをスキャンしてボットを認証する\",\n \"qqGetQrcode\": \"QQのQRコードを取得する\",\n \"qqScanHint\": \"上部のQRコードをQQでスキャンし、認証を完了してください。認証完了後、APP IDとClient Secretが自動的に入力されます。\",\n \"qqAuthSuccess\": \"QQボットの認証が完了しました。APP IDとClient Secretが自動的に入力されました。\",\n \"qqQrcodeFailed\": \"QQのQRコードの取得に失敗しました。しばらくしてからもう一度お試しください。\",\n \"qqQrcodeExpired\": \"QRコードの有効期限が切れています。再度取得してください。\",\n \"atSenderOnReply\": \"返信時に送信者を@メンション\",\n \"atSenderOnReplyTooltip\": \"有効にすると、ボットはグループチャットでの返信の最初のメッセージで送信者を@メンションします。注意:カードモードでは、@メンションは企業内部メンバーのみ有効です。\",\n \"dingtalkEndpoint\": \"APIエンドポイント\",\n \"dingtalkEndpointTooltip\": \"DingTalkプライベートデプロイ用のカスタムAPIエンドポイント。空欄の場合は公式デフォルトを使用します。\",\n \"wecomCancelled\": \"認証がキャンセルされました\",\n \"wecomAuthFailed\": \"認証に失敗しました: {{msg}}\",\n \"filterAll\": \"すべて\",\n \"builtin\": \"組み込み\",\n \"custom\": \"カスタム\",\n \"feishuRegion\": \"地域\",\n \"feishuRegionTooltip\": \"中国国内はFeishu、海外はLarkを選択\",\n \"feishuChina\": \"Feishu(中国)\",\n \"feishuInternational\": \"Lark(海外)\",\n \"feishuScanGuide\": \"FeishuでQRコードをスキャンしてボットを作成します。スキャン後、Feishuアプリが自動作成され、App IDとApp Secretが自動入力されます。\",\n \"feishuScanLogin\": \"スキャンしてボットを作成\",\n \"feishuGetQrcode\": \"Feishu QRコードを取得\",\n \"feishuScanHint\": \"上のQRコードをFeishuでスキャンしてください。認証後、App IDとApp Secretが自動入力されます。\",\n \"feishuAuthSuccess\": \"Feishuボットが作成されました。App IDとApp Secretが自動入力されました。\",\n \"feishuQrcodeExpired\": \"QRコードの有効期限が切れました。再取得してください。\",\n \"feishuQrcodeFailed\": \"Feishu QRコードの取得に失敗しました。もう一度お試しください。\",\n \"wechatSetupGuide\": \"WeChat個人アカウントBot(iLinkプロトコル)。Bot Tokenが未設定の場合、起動時にQRコードURLを表示します。スキャンして認証してください。Tokenはローカルに保存されます。\",\n \"wechatContextTokenLimit\": \"WeChat iLinkプラットフォームの制限:各ユーザーメッセージのcontext_tokenで返信できるメッセージは最大10件です。これはプラットフォーム側のハード制限です。制限超過による送信失敗を避けるため、思考およびツール出力を無効にするか、メッセージ統合機能を有効にすることをお勧めします。\",\n \"wechatBotToken\": \"Bot Token\",\n \"wechatBotTokenTooltip\": \"QRコードログイン後に取得したBearerトークン。空の場合は起動時にQRコードログインが案内されます。\",\n \"wechatBotTokenPlaceholder\": \"QRログイン後に自動入力、または手動で貼り付け\",\n \"wechatBotTokenFile\": \"Tokenファイルパス\",\n \"wechatBotTokenFileTooltip\": \"Botトークンの保存パス。デフォルトは ~/.qwenpaw/wechat_bot_token\",\n \"wechatMediaDir\": \"メディアディレクトリ\",\n \"wechatMessageMerge\": \"メッセージ統合\",\n \"wechatMessageMergeTooltip\": \"複数の返信メッセージを統合して送信し、WeChat の context_token あたり10件のメッセージ制限を回避します。遅延を0に設定すると、すべてのテキスト返信がリクエスト終了時に1つのメッセージに統合されます。正の遅延値(ミリ秒)は、その時間内の隣接メッセージを統合します。\",\n \"wechatMessageMergeDelayMs\": \"統合遅延(ミリ秒)\",\n \"wechatMessageMergeDelayMsTooltip\": \"隣接メッセージ統合の時間ウィンドウ。0に設定すると、すべてのメッセージを1つの最終返信に統合します。正の値(例: 1000)は、その期間中のメッセージをバッファリングしてから送信します。\",\n \"wechatMessageMergeDelayMsValidation\": \"有効な非負整数を入力してください\",\n \"wechatScanLogin\": \"QRコードでログイン\",\n \"wechatGetQrcode\": \"ログインQRコードを取得\",\n \"wechatScanHint\": \"WeChatでQRコードをスキャンして、完了後に保存してください\",\n \"wechatQrcodeFailed\": \"QRコードの取得に失敗しました。再試行してください\",\n \"wechatLoginSuccess\": \"WeChat ログイン成功、トークンが入力されました\",\n \"wechatQrcodeExpired\": \"QRコードが期限切れです。再取得してください\",\n \"shareSessionInGroup\": \"グループでコンテキスト共有\",\n \"shareSessionInGroupTooltip\": \"有効にすると、グループの全メンバーが同じ会話コンテキストを共有します。無効にすると、各メンバーが独立したコンテキストを持ちます。\",\n \"accessControlEnabled\": \"アクセス制御\",\n \"accessControlEnabledTooltip\": \"有効にすると、ホワイトリストのユーザーのみBotと対話できます。未知のユーザーにはIDが通知され、承認待ちリストに追加されます。\",\n \"accessControlDm\": \"DM アクセス制御\",\n \"accessControlDmTooltip\": \"有効にすると、ホワイトリストのユーザーのみDMでBotと対話できます\",\n \"accessControlGroup\": \"グループ アクセス制御\",\n \"accessControlGroupTooltip\": \"有効にすると、ホワイトリストのユーザーのみグループチャットでBotと対話できます\",\n \"dmDisabled\": \"DMを無効化\",\n \"dmDisabledTooltip\": \"有効にすると、Botはすべてのダイレクトメッセージを完全に無視します\",\n \"groupDisabled\": \"グループを無効化\",\n \"groupDisabledTooltip\": \"有効にすると、Botはすべてのグループメッセージを完全に無視します\",\n \"manageAccessControl\": \"アクセス制御\",\n \"pendingApprovals\": \"承認待ち\",\n \"whitelist\": \"ホワイトリスト\",\n \"blacklist\": \"ブラックリスト\",\n \"addUser\": \"ユーザーを追加\",\n \"addUserPlaceholder\": \"ユーザーIDを入力\",\n \"userId\": \"ユーザーID\",\n \"username\": \"ユーザー名\",\n \"channel\": \"チャンネル\",\n \"firstMessage\": \"最初のメッセージ\",\n \"time\": \"時間\",\n \"actions\": \"操作\",\n \"approve\": \"ホワイトリスト\",\n \"deny\": \"ブラックリスト\",\n \"dismiss\": \"無視\",\n \"noWhitelistUsers\": \"ホワイトリストユーザーなし\",\n \"noBlacklistUsers\": \"ブラックリストユーザーなし\",\n \"noPendingApprovals\": \"承認待ちなし\",\n \"userAdded\": \"ユーザーを追加しました\",\n \"userRemoved\": \"ユーザーを削除しました\",\n \"approveSuccess\": \"ホワイトリストに追加しました\",\n \"denySuccess\": \"ブラックリストに追加しました\",\n \"dismissSuccess\": \"無視しました\",\n \"operationFailed\": \"操作に失敗しました\",\n \"remark\": \"備考\",\n \"usernamePlaceholder\": \"ユーザー名を入力(任意)\",\n \"remarkPlaceholder\": \"備考を入力(任意)\",\n \"filterByChannel\": \"チャンネルで絞り込み\",\n \"selectToOperate\": \"項目を選択して操作\",\n \"selectedCount\": \"{{count}}件選択中\",\n \"batchApprove\": \"承認\",\n \"batchDeny\": \"ブラックリスト\",\n \"batchDismiss\": \"削除\",\n \"batchApproveConfirm\": \"{{count}}件のユーザーを承認しますか?\",\n \"batchDenyConfirm\": \"{{count}}件のユーザーをブラックリストに追加しますか?\",\n \"batchDismissConfirm\": \"{{count}}件のユーザーを削除しますか?\",\n \"batchSuccess\": \"{{count}}件のユーザーを処理しました\",\n \"batchRemove\": \"削除\",\n \"batchRemoveConfirm\": \"{{count}}件のユーザーを削除しますか?\",\n \"enabledSection\": \"有効\",\n \"enabledCount\": \"{{count}}件\",\n \"availableSection\": \"未有効\",\n \"enableAction\": \"有効にする\",\n \"noEnabledChannels\": \"有効なチャンネルはありません\",\n \"goEnableChannels\": \"チャンネルを有効にする\"\n },\n \"sessions\": {\n \"title\": \"セッション\",\n \"description\": \"アクティブなチャットセッションの表示と管理\",\n \"loading\": \"セッションを読み込み中...\",\n \"confirmDelete\": \"削除の確認\",\n \"deleteConfirm\": \"このセッションを削除してもよろしいですか?\",\n \"batchDeleteConfirm\": \"選択した {{count}} 件のセッションを削除してもよろしいですか?\",\n \"deleteSuccess\": \"セッションを削除しました\",\n \"deleteFailed\": \"セッションの削除に失敗しました\",\n \"batchDeleteSuccess\": \"{{count}} 件のセッションを削除しました\",\n \"batchDeleteFailed\": \"セッションの一括削除に失敗しました\",\n \"saveSuccess\": \"セッションを保存しました\",\n \"saveFailed\": \"セッションの保存に失敗しました\",\n \"batchDeleteButton\": \"一括削除\",\n \"filterTitle\": \"タイトルで絞り込み\",\n \"filterUserId\": \"ユーザーIDで絞り込み\",\n \"filterChannel\": \"チャンネルで絞り込み\",\n \"allChannels\": \"すべてのチャンネル\",\n \"totalItems\": \"合計 {{count}} 件\",\n \"selectedItems\": \"{{count}} 件選択中\",\n \"editSession\": \"セッションを編集\",\n \"pleaseInputName\": \"セッション名を入力してください\",\n \"sessionNamePlaceholder\": \"セッション名\",\n \"activeTab\": \"アクティブ\",\n \"archivedTab\": \"アーカイブ済み\",\n \"archivedAtColumn\": \"アーカイブ日時\",\n \"archive\": {\n \"action\": \"アーカイブ\",\n \"unaction\": \"アーカイブ解除\",\n \"batchAction\": \"一括アーカイブ\",\n \"batchUnaction\": \"一括アーカイブ解除\",\n \"success\": \"チャットをアーカイブしました\",\n \"successHint\": \"アーカイブしました。「コントロール>セッション管理>アーカイブ済み」で確認できます。\",\n \"failed\": \"アーカイブに失敗しました\",\n \"unarchiveSuccess\": \"アーカイブを解除しました\",\n \"unarchiveFailed\": \"アーカイブ解除に失敗しました\",\n \"batchSuccess\": \"{{count}} 件のチャットをアーカイブしました\",\n \"batchFailed\": \"一括アーカイブに失敗しました\",\n \"batchUnarchiveSuccess\": \"{{count}} 件のアーカイブを解除しました\",\n \"batchUnarchiveFailed\": \"一括アーカイブ解除に失敗しました\",\n \"groupLabel\": \"アーカイブ済み ({{count}})\",\n \"modalTitle\": \"アーカイブ済みのチャット\",\n \"unarchiveAll\": \"すべてアーカイブ解除\",\n \"noArchived\": \"アーカイブ済みのチャットはありません\"\n }\n },\n \"environments\": {\n \"parent\": \"設定\",\n \"environments\": \"環境変数\",\n \"title\": \"環境変数\",\n \"description\": \"エージェントとスキルのキー・バリュー形式の環境変数を設定します。\",\n \"key\": \"キー\",\n \"value\": \"値\",\n \"variableNamePlaceholder\": \"VARIABLE_NAME\",\n \"valuePlaceholder\": \"値\",\n \"insertRowBelow\": \"下に行を挿入\",\n \"deleteRow\": \"行を削除\",\n \"deleteVariable\": \"変数を削除\",\n \"deleteConfirm\": \"\\\"{{name}}\\\" を削除しますか?\",\n \"deleteSelected\": \"選択を削除\",\n \"deleteSelectedConfirm\": \"{{label}} を削除しますか?\",\n \"keyRequired\": \"キーは必須です\",\n \"invalidKeyFormat\": \"キーのフォーマットが不正です\",\n \"duplicateKey\": \"キーが重複しています\",\n \"saveSuccess\": \"環境変数を保存しました\",\n \"saveFailed\": \"保存に失敗しました\",\n \"deleteSuccess\": \"\\\"{{name}}\\\" を削除しました\",\n \"deleteFailed\": \"削除に失敗しました\",\n \"noVariables\": \"環境変数がまだ設定されていません。\",\n \"addVariable\": \"変数を追加\",\n \"loading\": \"読み込み中...\",\n \"retry\": \"再試行\",\n \"of\": \"/\",\n \"selected\": \"選択中\",\n \"variable\": \"変数\",\n \"variables\": \"変数\",\n \"showValue\": \"値を表示\",\n \"hideValue\": \"値を非表示\"\n },\n \"tokenUsage\": {\n \"title\": \"Token 使用量\",\n \"totalTokens\": \"合計 Token\",\n \"totalCalls\": \"総呼び出し回数\",\n \"promptTokens\": \"入力 Token\",\n \"completionTokens\": \"出力 Token\",\n \"byModel\": \"モデル別\",\n \"byDate\": \"日付別\",\n \"model\": \"モデル\",\n \"date\": \"日付\",\n \"loadFailed\": \"Token 使用量の読み込みに失敗しました\",\n \"noData\": \"選択期間に Token 使用データがありません\",\n \"modelTrend\": \"モデル使用量トレンド\",\n \"tokenTypeChart\": \"トークンタイプのトレンド\",\n \"selectAll\": \"すべて選択\",\n \"allSelected\": \"すべて\",\n \"itemsSelected\": \"{{count}} 件選択済み\",\n \"selectModels\": \"モデルを選択\",\n \"selectTokenTypes\": \"トークンタイプを選択\"\n },\n \"agentStats\": {\n \"title\": \"エージェント統計\",\n \"description\": \"選択期間のエージェント統計を確認します。\",\n \"totalSessions\": \"総セッション数\",\n \"totalMessages\": \"総メッセージ数\",\n \"userMessages\": \"ユーザーメッセージ\",\n \"assistantMessages\": \"アシスタントメッセージ\",\n \"promptTokens\": \"入力 Token\",\n \"completionTokens\": \"出力 Token\",\n \"llmCalls\": \"LLM 呼び出し\",\n \"toolCalls\": \"ツール呼び出し\",\n \"channel\": \"チャンネル\",\n \"sessionCount\": \"セッション数\",\n \"sessions\": \"セッション\",\n \"newSessions\": \"新規セッション\",\n \"activeSessions\": \"アクティブセッション\",\n \"messages\": \"メッセージ\",\n \"messageTrend\": \"メッセージ推移\",\n \"sessionTrend\": \"セッション推移\",\n \"tokenTrend\": \"Token 推移\",\n \"llmAndToolTrend\": \"LLM & ツール呼び出し推移\",\n \"sessionsByChannel\": \"チャンネル別セッション統計\",\n \"messagesByChannel\": \"チャンネル別メッセージ統計\",\n \"totalSessionsTooltip\": \"作成されたチャットセッションの総数。各セッションは独立した会話コンテキストです\",\n \"totalMessagesTooltip\": \"全セッションのメッセージ総数。ユーザーメッセージとアシスタントメッセージを含みます\",\n \"promptTokensTooltip\": \"LLM に送信されたプロンプト Token の総数\",\n \"completionTokensTooltip\": \"LLM によって生成された完了 Token の総数\",\n \"llmCallsTooltip\": \"LLM API 呼び出し回数。 各リクエストは1回の呼び出しとしてカウントされます\",\n \"toolCallsTooltip\": \"エージェントがツールを呼び出すことを決めた回数。1回のLLM呼び出しで複数のツール意図が発生する場合があります\",\n \"messageTrendTooltip\": \"ユーザーメッセージとアシスタントメッセージの比較推移\",\n \"sessionTrendTooltip\": \"新規セッションとアクティブセッションの比較推移。オレンジ色のバーはその日に作成されたセッション、青色のバーはその日にメッセージがあったセッションです\",\n \"tokenTrendTooltip\": \"入力 Token と出力 Token の消費推移\",\n \"llmAndToolTrendTooltip\": \"LLM 呼び出し回数とツール意図の比較推移\",\n \"sessionsByChannelTooltip\": \"メッセージチャンネル別のセッション分布。チャンネルに属さないセッションはデフォルトで Console にグループ化されます\",\n \"messagesByChannelTooltip\": \"メッセージチャンネル別のメッセージ総数分布\",\n \"loadFailed\": \"統計データの読み込みに失敗しました\",\n \"noData\": \"選択期間に統計データがありません\",\n \"retry\": \"再試行\"\n },\n \"modelSelector\": {\n \"selectModel\": \"モデルを選択\",\n \"noConfiguredModels\": \"設定済みモデルがありません\",\n \"noFreeModels\": \"利用可能な無料モデルがありません\",\n \"switchFailed\": \"モデルの切り替えに失敗しました\",\n \"searchModels\": \"モデルを検索...\",\n \"noModelsFound\": \"一致するモデルが見つかりません\",\n \"free\": \"無料\",\n \"vision\": \"視覚\",\n \"freeBannerText\": \"無料モデルには利用制限やサービス不安定の可能性があります。詳細はプロバイダーをご確認ください。\",\n \"moreProviders\": \"その他の無料プロバイダー(API Key が必要)\",\n \"proBannerText\": \"設定ページで構成済みのモデルのみ表示。\",\n \"connectToUse\": \"{{provider}} に接続して無料モデルを利用\",\n \"configureApiKey\": \"{{provider}} の API Key を設定して無料モデルを利用\",\n \"configureApiKeyTitle\": \"設定ページへ移動\",\n \"configureApiKeyConfirm\": \"モデル設定ページに移動して {{provider}} の API Key を設定します。続行しますか?\",\n \"goToConfigure\": \"設定へ\",\n \"noModelsDiscovered\": \"接続済み。モデル読み込み中...\",\n \"oauthTitle\": \"{{provider}} に接続\",\n \"oauthDescription\": \"{{provider}} への認証用に新しいブラウザウィンドウが開きます。APIキーは自動的に保存されます。\",\n \"oauthContinue\": \"続行\",\n \"oauthWaiting\": \"認証を待機中...\",\n \"oauthWaitingDescription\": \"ブラウザウィンドウで認証を完了してください。\",\n \"oauthConnected\": \"{{provider}} に正常に接続しました\",\n \"oauthFailed\": \"認証に失敗しました。もう一度お試しください\",\n \"viewMore\": \"もっと見る ({{count}})\"\n },\n \"models\": {\n \"llmConfiguration\": \"LLM設定\",\n \"providersTitle\": \"プロバイダー\",\n \"providersDescription\": \"各プロバイダーのAPIキーとエンドポイントを設定します。\",\n \"llmTitle\": \"モデル\",\n \"defaultLlm\": \"デフォルト LLM\",\n \"llmDescription\": \"ここではグローバル既定の LLM モデルを設定します。Chat ページでは各 Agent ごとに使用するモデルを個別に選択できます。\",\n \"configureProvider\": \"{{name}} を設定\",\n \"baseURL\": \"ベースURL\",\n \"apiKey\": \"APIキー\",\n \"advancedConfig\": \"詳細設定\",\n \"authMode\": \"認証モード\",\n \"authModeApiKey\": \"API Key (x-api-key)\",\n \"authModeAuthToken\": \"Auth Token (Authorization: Bearer)\",\n \"customHeaders\": \"カスタムヘッダー\",\n \"customHeadersHint\": \"すべての API リクエストに追加されます(例: x-app: cli)\",\n \"customHeaderKey\": \"ヘッダー名\",\n \"customHeaderValue\": \"ヘッダー値\",\n \"addHeader\": \"+ ヘッダーを追加\",\n \"generateConfig\": \"生成パラメータ設定\",\n \"generateConfigHint\": \"JSON 形式で指定した生成パラメータ設定です。展開されたうえで生成リクエスト(`openai.chat.completions` または `anthropic.messages`)に渡されます。\",\n \"generateConfigInvalidJson\": \"有効な JSON を入力してください\",\n \"generateConfigMustBeObject\": \"生成パラメータ設定は JSON オブジェクトである必要があります\",\n \"modelAdvancedConfig\": \"モデル詳細設定\",\n \"modelGenerateConfig\": \"モデル生成パラメータ\",\n \"modelGenerateConfigHint\": \"JSON 形式で指定したこのモデル専用の生成パラメータ設定です。展開されたうえで生成リクエストに渡されます。プロバイダーレベルの詳細設定より優先されます。\",\n \"modelConfigSaved\": \"モデル {{name}} の設定が保存されました\",\n \"modelConfigSaveFailed\": \"モデル設定の保存に失敗しました\",\n \"modelConfigLabel\": \"モデル設定\",\n \"maxTokensLabel\": \"最大出力トークン数\",\n \"maxTokensHint\": \"1回のレスポンスあたりの最大出力トークン数\",\n \"maxInputLengthLabel\": \"最大コンテキスト長\",\n \"maxInputLengthHint\": \"モデルのコンテキストウィンドウサイズ、コンテキスト圧縮のしきい値を制御します(≥1000)\",\n \"relayReasoningLabel\": \"思考内容を転送\",\n \"relayReasoningHint\": \"推論内容(reasoning contents)を後続の会話ターンに転送します\",\n \"thinkingModeLabel\": \"思考を有効化\",\n \"thinkingModeHint\": \"オンにすると思考/推論を有効化、オフにすると思考を無効化します。\",\n \"thinkingBudgetLabel\": \"思考バジェット\",\n \"thinkingBudgetHint\": \"モデルのデフォルト思考バジェットを使用\",\n \"switchToManual\": \"手動設定\",\n \"switchToAuto\": \"自動\",\n \"reasoningEffortLabel\": \"推論深度\",\n \"reasoningEffortHint\": \"推論の深さを制御(OpenAI系)\",\n \"protocol\": \"プロトコル\",\n \"protocolHint\": \"この設定のプロバイダーAPIプロトコルを選択してください。\",\n \"selectProtocol\": \"プロトコルを選択してください\",\n \"protocolOpenAI\": \"OpenAI互換(Chat Completions)\",\n \"protocolOpenAIResponse\": \"OpenAI互換(Response API)\",\n \"protocolAnthropic\": \"Anthropic(Messages API)\",\n \"currentKey\": \"現在: {{key}}\",\n \"startsWith\": \"\\\"{{prefix}}\\\" で始まる\",\n \"optionalSelfHosted\": \"セルフホストサービスの場合は任意\",\n \"leaveBlankKeep\": \"空欄のままにすると現在のキーを維持します\",\n \"enterApiKey\": \"APIキーを入力({{prefix}}...)\",\n \"enterApiKeyOptional\": \"APIキーを入力(任意)\",\n \"openAIEndpoint\": \"OpenAIエンドポイント、例: https://api.openai.com/v1\",\n \"openAICompatibleEndpoint\": \"OpenAI互換エンドポイント、例: https://api.example.com(プロバイダーが必要とする場合のみ /v1 を追加)\",\n \"azureEndpointHint\": \"Azure OpenAIエンドポイント、例: https://<resource>.openai.azure.com/openai/v1\",\n \"anthropicEndpointHint\": \"Anthropicエンドポイント、例: https://api.anthropic.com\",\n \"ollamaEndpointHint\": \"Ollamaエンドポイント、例: http://localhost:11434\",\n \"lmstudioEndpointHint\": \"LM Studioエンドポイント、例: http://localhost:1234/v1\",\n \"apiEndpointHint\": \"APIエンドポイント、例: https://api.example.com\",\n \"pleaseEnterBaseURL\": \"APIベースURLを入力してください\",\n \"selectBaseURL\": \"ベースURLを選択してください\",\n \"selectBaseURLHint\": \"このプロバイダーのリージョンエンドポイントを選択\",\n \"pleaseEnterValidURL\": \"有効なURLを入力してください\",\n \"apiKeyShouldStart\": \"APIキーは \\\"{{prefix}}\\\" で始まる必要があります\",\n \"configurationSaved\": \"{{name}} の設定を保存しました\",\n \"failedToSaveConfig\": \"設定の保存に失敗しました\",\n \"revokeAuthorization\": \"認証を取り消す\",\n \"revokeConfirmContent\": \"{{name}} のAPIキーを削除してもよろしいですか?現在のLLMモデル設定もクリアされます。\",\n \"revokeConfirmSimple\": \"{{name}} のAPIキーを削除してもよろしいですか?\",\n \"authorizationRevoked\": \"{{name}} の認証を取り消し、LLMモデルをクリアしました\",\n \"authorizationRevokedSimple\": \"{{name}} の認証を取り消しました\",\n \"failedToRevoke\": \"認証の取り消しに失敗しました\",\n \"active\": \"アクティブ: {{provider}} / {{model}}\",\n \"provider\": \"プロバイダー\",\n \"model\": \"モデル\",\n \"selectProvider\": \"プロバイダーを選択(認証済みである必要があります)\",\n \"selectModel\": \"モデルを選択\",\n \"llmModelUpdated\": \"LLMモデルを更新しました\",\n \"failedToSave\": \"保存に失敗しました\",\n \"freeModelWarningTitle\": \"無料モデルのご案内\",\n \"freeModelWarningMessage\": \"無料モデルはサービスが不安定になる場合があります。詳細はプロバイダーの利用規約をご確認ください:\",\n \"freeModelWarningDontShowAgain\": \"今後は表示しない\",\n \"saved\": \"保存しました\",\n \"save\": \"保存\",\n \"cancel\": \"キャンセル\",\n \"loading\": \"読み込み中...\",\n \"retry\": \"再試行\",\n \"notSet\": \"未設定\",\n \"notRequired\": \"不要\",\n \"available\": \"利用可能\",\n \"unavailable\": \"利用不可\",\n \"providerAvailable\": \"準備完了(モデルあり)\",\n \"providerNoModels\": \"未準備(モデルなし)\",\n \"search\": \"検索\",\n \"searchPlaceholder\": \"プロバイダーを検索...\",\n \"searchModelPlaceholder\": \"モデルを検索...\",\n \"providerNotConfigured\": \"未準備(未設定)\",\n \"settings\": \"設定\",\n \"actions\": \"操作\",\n \"custom\": \"カスタム\",\n \"builtin\": \"組み込み\",\n \"freeTierGroup\": \"無料枠\",\n \"paidGroup\": \"有料\",\n \"localCustomGroup\": \"ローカル & カスタム\",\n \"endpointCount\": \"{{count}} エンドポイント\",\n \"connect\": \"OAuth\",\n \"saveApiKey\": \"保存\",\n \"addProvider\": \"プロバイダーを追加\",\n \"addProviderTitle\": \"カスタムプロバイダーを追加\",\n \"providerIdLabel\": \"プロバイダーID\",\n \"providerIdPlaceholder\": \"例: openai, google, anthropic\",\n \"providerIdHint\": \"小文字・数字・ハイフン・アンダースコアが使用できます。後から変更できません。\",\n \"providerNameLabel\": \"表示名\",\n \"providerNamePlaceholder\": \"例: OpenAI, Google Gemini\",\n \"defaultBaseUrlLabel\": \"デフォルトベースURL\",\n \"defaultBaseUrlPlaceholder\": \"例: https://api.example.com\",\n \"apiKeyPrefixLabel\": \"APIキープレフィックス(任意)\",\n \"apiKeyPrefixPlaceholder\": \"例: sk-\",\n \"providerCreated\": \"プロバイダー \\\"{{name}}\\\" を作成しました\",\n \"providerCreateFailed\": \"プロバイダーの作成に失敗しました\",\n \"deleteProvider\": \"プロバイダーを削除\",\n \"deleteProviderConfirm\": \"カスタムプロバイダー \\\"{{name}}\\\" とすべてのモデルを削除しますか?この操作は取り消せません。\",\n \"providerDeleted\": \"プロバイダー \\\"{{name}}\\\" を削除しました\",\n \"providerDeleteFailed\": \"プロバイダーの削除に失敗しました\",\n \"manageModels\": \"モデル\",\n \"models\": \"モデル\",\n \"manageModelsTitle\": \"{{provider}} — モデル管理\",\n \"userAdded\": \"ユーザー追加\",\n \"addModel\": \"モデルを追加\",\n \"addModels\": \"モデルを追加\",\n \"autoDiscoverModels\": \"モデルを自動検出\",\n \"addModelTitle\": \"{{provider}} にモデルを追加\",\n \"modelIdLabel\": \"モデルID\",\n \"modelIdPlaceholder\": \"例: gpt-4o, gemini-2.0-flash\",\n \"modelDiscoveryUnavailableHint\": \"利用可能なモデルが見つかりませんでした。このプロバイダーはモデル一覧の取得に対応していない可能性があるため、モデルIDを手動で入力してください。\",\n \"modelNameLabel\": \"モデル名\",\n \"modelNameRequired\": \"モデル名を入力してください\",\n \"modelNamePlaceholder\": \"例: GPT-4o, Gemini 2.0 Flash\",\n \"modelAdded\": \"モデル \\\"{{name}}\\\" を追加しました\",\n \"modelAddFailed\": \"モデルの追加に失敗しました\",\n \"modelAlreadyExists\": \"モデル ID \\\"{{id}}\\\" は既に存在します\",\n \"autoDiscoverModelsSuccess\": \"自動検出が完了しました。{{count}} 件のモデルを追加しました\",\n \"autoDiscoverModelsNoNew\": \"自動検出が完了しました。新しいモデルは追加されませんでした\",\n \"autoDiscoverModelsFailed\": \"モデルの自動検出に失敗しました\",\n \"removeModel\": \"削除\",\n \"removeModelConfirm\": \"{{provider}} からモデル \\\"{{name}}\\\" を削除しますか?\",\n \"modelRemoved\": \"モデル \\\"{{name}}\\\" を削除しました\",\n \"modelRemoveFailed\": \"モデルの削除に失敗しました\",\n \"loadMore\": \"もっと読み込む ({{count}})\",\n \"modelsCount\": \"{{count}} モデル\",\n \"noModels\": \"モデルなし\",\n \"addModelFirst\": \"先にモデルを追加してください\",\n \"local\": \"ローカル\",\n \"localType\": \"タイプ\",\n \"localEmbedded\": \"組み込み(インプロセス)\",\n \"localDownloadFirst\": \"モデルサービスは未起動です\",\n \"localDownloadModel\": \"モデルをダウンロード\",\n \"localModelsTitle\": \"{{provider}} — ローカルモデル\",\n \"localNoModels\": \"まだダウンロード済みモデルがありません\",\n \"localRepoId\": \"モデルリポジトリID\",\n \"localRepoIdRequired\": \"モデルリポジトリIDを入力してください\",\n \"localRepoIdPlaceholder\": \"例: Qwen/Qwen3-0.6B-GGUF\",\n \"localFilename\": \"ファイル名(任意)\",\n \"localFilenamePlaceholder\": \"例: mistral-7b.Q4_K_M.gguf\",\n \"localFilenameHint\": \"空欄にすると最適な量子化が自動選択されます\",\n \"localSource\": \"ソース\",\n \"localDownloadSuccess\": \"モデルのダウンロードに成功しました\",\n \"localDownloadFailed\": \"モデルのダウンロードに失敗しました\",\n \"localDeleteModel\": \"モデルを削除\",\n \"localDeleteConfirm\": \"ローカルモデル \\\"{{name}}\\\" を削除しますか?モデルファイルはディスクから削除されます。\",\n \"localModelDeleted\": \"モデル \\\"{{name}}\\\" を削除しました\",\n \"localDeleteFailed\": \"モデルの削除に失敗しました\",\n \"localDownloadPending\": \"ダウンロードの準備中...\",\n \"localDownloading\": \"{{repo}} をダウンロード中... 数分かかる場合があります。\",\n \"localDownloadNavigateHint\": \"他のページに移動しても構いません。ダウンロードはバックグラウンドで継続されます。\",\n \"localDownloadInProgress\": \"すでにダウンロードが進行中です\",\n \"localDownloaded\": \"ダウンロード済み\",\n \"localNotDownloaded\": \"未ダウンロード\",\n \"localNoRecommendedModels\": \"現在のマシン性能ではローカルモデルの実行に向いていません\",\n \"localCancelDownloadTitle\": \"ダウンロードをキャンセル\",\n \"localCancelDownloadAction\": \"ダウンロードをキャンセル\",\n \"localCancelDownloadConfirm\": \"\\\"{{repo}}\\\" のダウンロードをキャンセルしますか?\",\n \"localDownloadCancelled\": \"ダウンロードをキャンセルしました\",\n \"localCancelDownloadFailed\": \"ダウンロードのキャンセルに失敗しました\",\n \"localLlamacppName\": \"推論エンジン\",\n \"localLlamacppInstallHint\": \"モデルのダウンロードや起動の前にローカルランタイムをインストールしてください。\",\n \"localInstallLlamacpp\": \"llama.cpp をインストール\",\n \"localLlamacppInstallStarted\": \"llama.cpp のダウンロードを開始しました\",\n \"localLlamacppInstallSuccess\": \"llama.cpp のインストールが完了しました\",\n \"localLlamacppInstallFailed\": \"llama.cpp のインストールに失敗しました\",\n \"localRuntimeSectionTitle\": \"Inference Engine\",\n \"localRuntimeSectionDescription\": \"Powered by Llama.cpp\",\n \"localRuntimeComputeHint\": \"デフォルトでは CPU を使用します。GPU アクセラレーションが必要な場合は、Ollama または LM Studio を使用してください。\",\n \"localRuntimeInstalled\": \"インストール済み\",\n \"localRuntimeUpdateAvailable\": \"更新可能\",\n \"localRuntimeUpdateAction\": \"クリックして最新バージョンをダウンロード\",\n \"localRuntimeUpdateConfirmTitle\": \"llama.cpp の更新を確認\",\n \"localRuntimeUpdateConfirmContent\": \"更新すると、現在インストールされている llama.cpp が上書きされます。確認すると最新バージョンのダウンロードとインストールを開始します。\",\n \"localRuntimeUpdateConfirmContentWithServer\": \"更新すると、現在インストールされている llama.cpp が上書きされ、現在実行中のモデルサービス({{model}})が停止します。確認すると最新バージョンのダウンロードとインストールを開始します。\",\n \"localRuntimeUnsupported\": \"未対応\",\n \"localRuntimeMissing\": \"未導入\",\n \"localServerRunningTitle\": \"現在のローカルサーバー\",\n \"localServerOnline\": \"稼働中\",\n \"localServerIdle\": \"停止\",\n \"localServerUnavailable\": \"異常\",\n \"localNoActiveModel\": \"実行中のモデルはありません\",\n \"localNoDownloadedModelsHint\": \"ローカル推論サービスを起動する前に、適切なモデルをダウンロードしてください。\",\n \"localDownloadModelFirst\": \"先にモデルをダウンロードしてください\",\n \"localEngineInstallStateLabel\": \"導入\",\n \"localEngineRunStateLabel\": \"状態\",\n \"localEngineCurrentModelLabel\": \"現在提供中のモデル\",\n \"localEngineNextStepLabel\": \"次のステップ\",\n \"localEngineHealthLabel\": \"エンジン状態\",\n \"localEngineStatusHint\": \"下の一覧から適切なモデルをダウンロードして起動してください\",\n \"localStartServer\": \"起動\",\n \"localStopServer\": \"停止\",\n \"localServerStartFailed\": \"ローカルサーバーの起動に失敗しました\",\n \"localServerStopFailed\": \"ローカルサーバーの停止に失敗しました\",\n \"localServerSwitchTitle\": \"実行中モデルを切り替え\",\n \"localServerSwitchConfirm\": \"現在 {{current}} が実行中です。{{next}} に切り替えますか?\",\n \"localSwitchModel\": \"切り替え\",\n \"localModelPathMissing\": \"このダウンロード済みモデルにはローカルパス情報がありません。\",\n \"localModelsSectionTitle\": \"ローカルモデル\",\n \"localModelsSectionDescription\": \"ダウンロード済みモデルはすぐに起動できます。同時に実行できるのは 1 つのモデルダウンロードと 1 つのローカルサーバーだけです。\",\n \"localModelsLockedHint\": \"先に llama.cpp をインストールしてください\",\n \"localAlternativeRuntimeHint\": \"Ollama または LM Studio をお試しください\",\n \"localModelsCount\": \"ダウンロード済み {{downloaded}} / {{total}}\",\n \"localCurrentDownloadTitle\": \"現在のダウンロード\",\n \"localAdvancedConfigTitle\": \"ローカルモデル詳細設定\",\n \"localAdvancedConfigDescription\": \"これらの設定は組み込みのローカル推論ランタイムに適用されます。最大コンテキスト長の変更は、次回ローカルサーバー起動時に反映されます。\",\n \"localAdvancedConfigSaved\": \"ローカルモデル詳細設定を保存しました\",\n \"localAdvancedConfigSaveFailed\": \"ローカルモデル詳細設定の保存に失敗しました\",\n \"localMaxContextLengthLabel\": \"最大コンテキスト長\",\n \"localMaxContextLengthHint\": \"モデルのコンテキストウィンドウで扱える最大トークン数です。各リクエストに含められる履歴長を決定します。変更は次回ローカルサーバー起動時に反映されます。\",\n \"localMaxContextLengthRequired\": \"最大コンテキスト長を入力してください\",\n \"localMaxContextLengthMin\": \"最大コンテキスト長は {{min}} 以上である必要があります\",\n \"localMaxContextLengthPlaceholder\": \"最大コンテキスト長を入力\",\n \"localServerPortLabel\": \"サーバーポート\",\n \"localServerPortHint\": \"空欄の場合、llama.cpp は起動時に利用可能なポートを自動選択します。ポート番号を入力すると、以後の起動でその固定ポートを再利用します。変更は次回ローカルサーバー起動時に反映されます。\",\n \"localServerPortRange\": \"サーバーポートは {{min}} から {{max}} の範囲で指定してください\",\n \"localServerPortPlaceholder\": \"空欄で自動ポート選択\",\n \"localGenerateConfigHint\": \"ローカルモデルの生成パラメータを JSON オブジェクトで設定します。後続の生成リクエストに展開して渡されます。\",\n \"localCustomModelTitle\": \"カスタムモデルのダウンロード\",\n \"localCustomModelHint\": \"モデルリポジトリIDを入力します。GGUF形式のみ対応。\",\n \"localSourceHuggingFace\": \"Hugging Face\",\n \"localSourceModelScope\": \"ModelScope\",\n \"ollamaModelNamePlaceholder\": \"例: mistral:7b, qwen3:8b\",\n \"testConnection\": \"接続テスト\",\n \"testConnectionSuccess\": \"接続テストに成功しました\",\n \"testConnectionFailed\": \"接続テストに失敗しました\",\n \"testConnectionFailedWithMessage\": \"接続テストに失敗しました: {{message}}\",\n \"testConnectionError\": \"接続テスト中にエラーが発生しました\",\n \"discoverModels\": \"モデルを検出\",\n \"discoverModelsFailed\": \"モデルの検出に失敗しました\",\n \"ollamaFetchModelsFailed\": \"Ollamaモデルの取得に失敗しました。エラーの詳細を確認してください。\",\n \"modelTestFailed\": \"モデルの検証に失敗しました。モデルIDが正しいか確認してください\",\n \"modelTestFailedConfirm\": \"モデルの接続テストに失敗しました: {{message}}。このモデルを追加しますか?\",\n \"autoDiscoveredAndAdded\": \"{{count}} 件のモデルを自動検出し、{{added}} 件の新規モデルを追加しました\",\n \"autoDiscoveredNoNew\": \"{{count}} 件のモデルを自動検出しました。モデルリストはすでに最新です\",\n \"autoDiscoverFailed\": \"モデルの自動検出に失敗しました。手動でモデルを追加できます\",\n \"tagImage\": \"画像\",\n \"tagVideo\": \"動画\",\n \"tagTextOnly\": \"テキストのみ\",\n \"tagMultimodal\": \"マルチモーダル\",\n \"tagVision\": \"ビジョン\",\n \"tagText\": \"テキスト\",\n \"free\": \"無料\",\n \"tagNotProbed\": \"未検出\",\n \"probeMultimodal\": \"マルチモーダルテスト\",\n \"probeImage\": \"画像\",\n \"probeVideo\": \"動画\",\n \"probeSupported\": \"対応: {{types}}\",\n \"probeNotSupported\": \"このモデルはマルチモーダル入力に対応していません\",\n \"probeFailed\": \"検出に失敗しました\",\n \"modalityVision\": \"画像\",\n \"modalityAudio\": \"音声\",\n \"modalityVideo\": \"動画\",\n \"modalityFile\": \"ファイル\",\n \"modalityText\": \"テキストのみ\",\n \"searchProviderPlaceholder\": \"プロバイダー名を検索\",\n \"selectAllProviders\": \"すべて選択\",\n \"clearProviderSelection\": \"すべて解除\",\n \"noMatchingProviders\": \"一致するプロバイダーがありません\",\n \"filterFreeOnly\": \"無料モデルのみ\"\n },\n \"agentConfig\": {\n \"title\": \"設定\",\n \"description\": \"エージェントのランタイムパラメーターを設定します\",\n \"reactAgentTitle\": \"ReActエージェント\",\n \"llmRetryTitle\": \"LLM自動再試行\",\n \"llmRetryEnabled\": \"自動再試行を有効化\",\n \"llmRetryEnabledTooltip\": \"レート制限、タイムアウト、接続エラーなどの一時的なLLM API失敗を自動で再試行します。保存後、新しいリクエストに適用されます。\",\n \"llmMaxRetries\": \"最大再試行回数\",\n \"llmMaxRetriesTooltip\": \"再試行可能なLLMエラーが発生した場合の追加再試行回数の上限です。\",\n \"llmMaxRetriesPlaceholder\": \"最大再試行回数を入力\",\n \"llmMaxRetriesRequired\": \"最大再試行回数は必須です\",\n \"llmMaxRetriesMin\": \"最大再試行回数は1以上である必要があります\",\n \"llmBackoffBase\": \"バックオフ初期待機時間(秒)\",\n \"llmBackoffBaseTooltip\": \"最初の再試行までの基本待機時間です。以降は指数バックオフで増加します。\",\n \"llmBackoffBasePlaceholder\": \"初期待機時間を入力\",\n \"llmBackoffBaseRequired\": \"バックオフ初期待機時間は必須です\",\n \"llmBackoffBaseMin\": \"バックオフ初期待機時間は0.1秒以上である必要があります\",\n \"llmBackoffCap\": \"バックオフ最大待機時間(秒)\",\n \"llmBackoffCapTooltip\": \"指数バックオフの待機時間の上限です。\",\n \"llmBackoffCapPlaceholder\": \"最大待機時間を入力\",\n \"llmBackoffCapRequired\": \"バックオフ最大待機時間は必須です\",\n \"llmBackoffCapMin\": \"バックオフ最大待機時間は0.5秒以上である必要があります\",\n \"llmBackoffCapGteBase\": \"バックオフ最大待機時間は初期待機時間以上である必要があります\",\n \"llmRateLimiterTitle\": \"LLMレートリミッター\",\n \"llmMaxConcurrent\": \"最大同時リクエスト数\",\n \"llmMaxConcurrentTooltip\": \"同時に実行できるLLMリクエストの上限です。すべてのAgentで共有されます。変更は再起動後に反映されます。\",\n \"llmMaxConcurrentPlaceholder\": \"最大同時リクエスト数を入力\",\n \"llmMaxConcurrentRequired\": \"最大同時リクエスト数は必須です\",\n \"llmMaxConcurrentRange\": \"最大同時リクエスト数は1以上である必要があります\",\n \"llmMaxQpm\": \"最大クエリ数/分(QPM)\",\n \"llmMaxQpmTooltip\": \"60秒スライディングウィンドウ内で許可される最大リクエスト数です。上限を超えるリクエストはディスパッチ前に待機し、429レート制限エラーを未然に防ぎます。0 = 無制限。\",\n \"llmMaxQpmPlaceholder\": \"最大QPMを入力(0 = 無制限)\",\n \"llmMaxQpmRequired\": \"最大QPMは必須です\",\n \"llmMaxQpmRange\": \"最大QPMは0以上である必要があります\",\n \"llmRateLimitPause\": \"レート制限一時停止(秒)\",\n \"llmRateLimitPauseTooltip\": \"429レート制限レスポンスを受信した際に適用されるデフォルトの一時停止時間(秒)です。APIのRetry-Afterヘッダーが存在する場合はそちらが優先されます。\",\n \"llmRateLimitPausePlaceholder\": \"一時停止時間を入力\",\n \"llmRateLimitPauseRequired\": \"レート制限一時停止時間は必須です\",\n \"llmRateLimitPauseMin\": \"レート制限一時停止時間は1秒以上である必要があります\",\n \"llmRateLimitJitter\": \"ジッター範囲(秒)\",\n \"llmRateLimitJitterTooltip\": \"一時停止時間に追加されるランダムなジッター範囲(秒)です。同時待機者の起床をずらすことで新たなバーストを防ぎます。\",\n \"llmRateLimitJitterPlaceholder\": \"ジッター範囲を入力\",\n \"llmRateLimitJitterRequired\": \"ジッター範囲は必須です\",\n \"llmRateLimitJitterMin\": \"ジッター範囲は0秒以上である必要があります\",\n \"llmAcquireTimeout\": \"スロット取得タイムアウト(秒)\",\n \"llmAcquireTimeoutTooltip\": \"レートリミタースロットの取得を待機する最大時間(秒)です。タイムアウト後はエラーが発生します。\",\n \"llmAcquireTimeoutPlaceholder\": \"タイムアウト時間を入力\",\n \"llmAcquireTimeoutRequired\": \"スロット取得タイムアウトは必須です\",\n \"llmAcquireTimeoutMin\": \"スロット取得タイムアウトは10秒以上である必要があります\",\n \"llmAcquireTimeoutGtPauseJitter\": \"スロット取得タイムアウトはレート制限一時停止とジッターの合計より大きい必要があります\",\n \"contextManagementTitle\": \"コンテキスト管理\",\n \"memoryManagerBackend\": \"長期記憶管理バックエンド\",\n \"memoryManagerBackendTooltip\": \"メモリマネージャーのバックエンドタイプ。remelight(ローカルファイル)または adbpg(クラウドデータベース)から選択できます。\",\n \"memoryManagerBackendRestartWarning\": \"長期記憶管理バックエンドの切り替えはホットリロードに対応していません。変更を反映するには保存後に QwenPaw を再起動してください。\",\n \"maxIters\": \"最大反復回数\",\n \"maxItersTooltip\": \"ReActエージェントの推論・行動サイクルの最大反復回数\",\n \"maxItersPlaceholder\": \"最大反復回数を入力\",\n \"maxItersRequired\": \"最大反復回数は必須です\",\n \"maxItersMin\": \"最大反復回数は1以上である必要があります\",\n \"agentLoopTitle\": \"エージェント Loop 設定\",\n \"loopMode\": {\n \"builtIn\": \"組み込み\",\n \"builtInNote\": \"パイプライン固定 · 値は編集可能\",\n \"templateSectionTitle\": \"Loop テンプレート\",\n \"templateSectionDescription\": \"組み込みテンプレートを選択するか、独自に追加します。\",\n \"defaultTab\": \"デフォルト\",\n \"goalTab\": \"ゴール\",\n \"missionTab\": \"ミッション\",\n \"defaultDescription\": \"明示的なモード以外で使用する標準の保護付き ReAct Loop。\",\n \"goalDescription\": \"具体的で検証可能なゴール向けの持続 Loop。\",\n \"missionDescription\": \"長時間の複数ステップミッション向けパイプライン。\",\n \"gatePipeline\": \"Gate パイプライン\",\n \"repetitionTitle\": \"反復保護\",\n \"repetitionDescription\": \"繰り返されるツール呼び出しを検出して介入します。\",\n \"iterationTitle\": \"反復回数制限\",\n \"iterationDescription\": \"ReAct の反復回数を制限します。\",\n \"retryTitle\": \"定性的完了チェック\",\n \"retryDescription\": \"ツール呼び出しを含まないテキスト応答のときだけ実行します。\",\n \"goalTurnTitle\": \"ゴールのターン制限\",\n \"goalTurnDescription\": \"アクティブなゴール内のターン数を制限します。\",\n \"maxGoalTurns\": \"ゴールの最大ターン数\",\n \"goalBudgetTitle\": \"ゴールのトークン予算\",\n \"goalBudgetDescription\": \"ゴール全体が予算に達したら停止します。\",\n \"maxGoalTokens\": \"ゴールの最大トークン数\",\n \"goalCompletionTitle\": \"ゴール完了チェック\",\n \"goalCompletionDescription\": \"停止前に明示的なゴール状態を確認します。\",\n \"goalCompletionReadOnly\": \"完了は組み込みのゴール状態プロトコルで制御されます。\",\n \"missionProgressTitle\": \"ミッション進捗チェック\",\n \"missionProgressDescription\": \"全ストーリーが通過するか上限まで継続します。\",\n \"missionProgressReadOnly\": \"完了は組み込みの Mission Story 状態で制御されます。\",\n \"missionIterationTitle\": \"Mission の反復上限\",\n \"missionIterationDescription\": \"新しい Mission の既定の実行上限を設定します。\",\n \"workerAttemptsTitle\": \"Worker の試行回数\",\n \"workerAttemptsDescription\": \"Story の検証失敗後の Worker 再試行回数を制限します。\",\n \"missionVerificationTitle\": \"Mission の検証ポリシー\",\n \"missionVerificationDescription\": \"既定の検証ガイダンスとテストコマンドを設定します。\",\n \"defaultMaxMissionIterations\": \"既定の最大実行ラウンド数\",\n \"defaultMaxMissionIterationsTooltip\": \"/mission で --max-iterations が未指定の場合に使用します。\",\n \"maxRetriesPerStory\": \"Story ごとの最大再試行回数\",\n \"maxRetriesPerStoryTooltip\": \"Verifier が失敗または部分完了を返した後の Worker 再試行回数です。\",\n \"defaultVerificationInstructions\": \"検証ガイダンス(任意)\",\n \"defaultVerificationInstructionsHelp\": \"すべての Mission Verifier に適用する自然言語の指示です。\",\n \"defaultVerificationInstructionsPlaceholder\": \"例:Windows のパス処理と実際の UI 表示を確認してください。\",\n \"defaultVerifyCommand\": \"既定のテストコマンド(任意)\",\n \"defaultVerifyCommandHelp\": \"Verifier は通常のツールと権限で実行を試みます。/mission --verify で上書きできます。\",\n \"defaultVerifyCommandPlaceholder\": \"例:pytest -q\",\n \"enableRepetitionProtection\": \"反復保護を有効化\",\n \"removeRule\": \"ルールを削除\",\n \"custom\": \"カスタム\",\n \"customDescription\": \"QwenPaw 組み込み Gate で保存可能なパイプラインを構築します。\",\n \"duplicate\": \"複製\",\n \"delete\": \"削除\",\n \"displayName\": \"表示名\",\n \"duplicateModeName\": \"同じ名前の Loop モードが既に存在します。\",\n \"slashCommand\": \"スラッシュコマンド\",\n \"description\": \"説明\",\n \"availableToAgent\": \"このエージェントで使用可能\",\n \"runsTopToBottom\": \"上から下へ実行\",\n \"addGate\": \"Gate を追加\",\n \"emptyPipeline\": \"Gate を1つ以上追加すると、このモードを使用できます。\",\n \"iterationGateTitle\": \"反復回数制限\",\n \"iterationGateDescription\": \"指定回数の Loop 後に停止します。\",\n \"doomGateTitle\": \"反復保護\",\n \"doomGateDescription\": \"繰り返しを検出して戦略を変更します。\",\n \"tokenGateTitle\": \"トークン予算\",\n \"tokenGateDescription\": \"入力と出力のトークン使用量を制限します。\",\n \"timeoutGateTitle\": \"Loop 時間制限\",\n \"timeoutGateDescription\": \"時間超過後、実行中の処理を中断せず次の Loop 境界で停止します。\",\n \"toolBudgetGateTitle\": \"ツール呼び出し予算\",\n \"toolBudgetGateDescription\": \"全体および指定ツールの呼び出しを制限します。\",\n \"retryGateTitle\": \"早期終了の再試行\",\n \"retryGateDescription\": \"終了前に再確認を促します。\",\n \"rubricGateTitle\": \"完了ルーブリック\",\n \"rubricGateDescription\": \"明示的な基準を評価し、必要なら修正します。\",\n \"qualitativeRubricTitle\": \"定性的完了チェック\",\n \"qualitativeRubricDescription\": \"ツール呼び出しのないテキスト応答を自然言語の基準で確認します。\",\n \"qualitativeRubric\": \"自然言語の完了基準\",\n \"qualitativeRubricHelp\": \"ツール呼び出しのないテキスト応答でのみ実行し、作業が残る場合は続行させます。\",\n \"completionRubricTitle\": \"完了信号チェック\",\n \"completionRubricDescription\": \"ツール呼び出しのないテキスト応答で完了信号を確認します。\",\n \"completionRubricHelp\": \"ツール呼び出しのないテキスト応答後に実行します。完了時は完了信号を出力し、未完了時は最大判定回数に達するまで作業を続行します。\",\n \"completionPrompt\": \"完了判定プロンプト\",\n \"completionPromptHelp\": \"完了と見なす条件を記述します。完了信号の出力指示は QwenPaw が自動的に挿入します。\",\n \"completionSignal\": \"完了信号\",\n \"completionSignalHelp\": \"QwenPaw はこの信号と、この信号だけを出力する指示を判定プロンプトに挿入します。前後の空白と大文字小文字を無視した完全一致のみが Loop を終了します。\",\n \"removeCriterion\": \"基準 {{number}} を削除\",\n \"totalTokensHelp\": \"このターンで使用する入力・出力トークンの合計上限です。\",\n \"promptTokensHelp\": \"システム指示、会話履歴、ツール定義、ツール結果を含みます。\",\n \"completionTokensHelp\": \"このターンでモデルが生成するトークンの上限です。\",\n \"maxIterations\": \"最大反復回数\",\n \"historyWindow\": \"履歴ウィンドウ\",\n \"similarityThreshold\": \"類似度しきい値\",\n \"totalTokens\": \"最大総トークン\",\n \"promptTokens\": \"最大入力トークン\",\n \"completionTokens\": \"最大出力トークン\",\n \"maxSeconds\": \"最大秒数\",\n \"maxSecondsHelp\": \"現在のモデル応答またはツール呼び出しの完了後に時間を確認し、実行中の処理は中断しません。\",\n \"allToolCalls\": \"全ツール呼び出し\",\n \"perToolLimits\": \"ツール別制限\",\n \"toolName\": \"ツール名\",\n \"callLimitAria\": \"{{name}} の呼び出し上限\",\n \"removeToolLimitAria\": \"{{name}} の制限を削除\",\n \"addToolLimit\": \"ツール別制限を追加\",\n \"maxRetries\": \"最大再試行回数\",\n \"retryInstruction\": \"再試行の指示\",\n \"passThreshold\": \"合格しきい値\",\n \"maxEvaluations\": \"最大判定回数\",\n \"graderError\": \"評価エラー\",\n \"stopSafely\": \"安全に停止\",\n \"verifyOnceMore\": \"もう一度検証\",\n \"completionCriteria\": \"完了基準\",\n \"criterionPlaceholder\": \"観測可能な完了条件を記述\",\n \"required\": \"必須\",\n \"weight\": \"重み\",\n \"addCriterion\": \"基準を追加\",\n \"moveGateAria\": \"{{title}} を移動\",\n \"removeGate\": \"Gate を削除\",\n \"newModeName\": \"新しい Loop モード\",\n \"defaultCustomDescription\": \"カスタム Gate パイプライン。\",\n \"copyName\": \"{{name}} のコピー\",\n \"createModeAria\": \"カスタム Loop モードを作成\",\n \"createModeTitle\": \"カスタム Loop モードを作成\",\n \"startingTemplate\": \"開始テンプレート\",\n \"startingPipeline\": \"開始パイプライン\",\n \"blankPipelineHint\": \"空の状態から始め、後でゲートを追加\",\n \"safeTemplate\": \"安全な実行\",\n \"researchTemplate\": \"予算付き調査\",\n \"qualityTemplate\": \"品質優先\",\n \"blankTemplate\": \"空のパイプライン\"\n },\n \"doomLoopEnabled\": \"繰り返し行動保護\",\n \"doomLoopEnabledTooltip\": \"エージェントが同じ操作を繰り返している場合に自動的に介入します\",\n \"doomLoopAfter\": \"連続\",\n \"doomLoopRepetitions\": \"回の同一操作後\",\n \"doomLoopStopAction\": \"一時停止して確認を待つ\",\n \"doomLoopWarnAction\": \"リマインダーを送信\",\n \"doomLoopSimpleMode\": \"シンプル\",\n \"doomLoopAdvancedMode\": \"詳細\",\n \"doomLoopWindowSize\": \"検出範囲\",\n \"doomLoopWindowSizeTooltip\": \"繰り返し検出のために最近何回の操作をチェックするか\",\n \"doomLoopSimilarity\": \"マッチ感度\",\n \"doomLoopSimilarityTooltip\": \"操作がどの程度類似していれば繰り返しとみなすか(低いほど厳格)\",\n \"doomLoopStages\": \"介入ルール\",\n \"doomLoopAction\": \"アクション\",\n \"doomLoopPrompt\": \"メッセージ\",\n \"doomLoopPromptPlaceholder\": \"リマインダーメッセージまたは一時停止の理由...\",\n \"doomLoopAddStage\": \"ルールを追加\",\n \"iterationTitle\": \"イテレーション制限\",\n \"iterationEnabled\": \"イテレーション制限を有効化\",\n \"iterationEnabledTooltip\": \"固定回数のループ後にエージェントを停止\",\n \"iterationMaxIterations\": \"最大イテレーション数\",\n \"iterationMaxIterationsTooltip\": \"停止するまでの最大ループ回数\",\n \"rubricTitle\": \"定性的完了チェック\",\n \"rubricEnabled\": \"定性的完了チェックを有効化\",\n \"rubricEnabledTooltip\": \"ツール呼び出しを含まないテキスト応答のときだけ実行します。\",\n \"rubricPrompt\": \"自然言語の完了基準\",\n \"rubricPromptTooltip\": \"タスク完了評価を求めるために注入されるプロンプト\",\n \"rubricDesc\": \"ツール呼び出しのないテキスト応答に自然言語の完了基準を適用し、作業が残る場合は続行させます。\",\n \"rubricPromptPlaceholder\": \"前のターンでツールを呼び出しませんでした。タスクが完了している場合は確認してください。そうでなければツールを使って続行してください。\",\n \"rubricMaxInterventions\": \"最大判定回数\",\n \"rubricMaxInterventionsTooltip\": \"1ターン内で、ツール呼び出しのないテキスト応答に対して行う完了チェックの最大回数です。\",\n \"inLoopModes\": \"/goal・/mission モードでも有効化\",\n \"inLoopModesTooltip\": \"/goal または /mission ループモードがアクティブな時もこのゲートを適用\",\n \"comingSoonEditable\": \"カスタム設定は将来のリリースで利用可能になります。\",\n \"simpleMode\": \"シンプル\",\n \"advancedMode\": \"詳細\",\n \"comingSoon\": \"近日公開\",\n \"reactModeTab\": \"Loopテンプレート - デフォルト\",\n \"goalModeTab\": \"Loopテンプレート - Goal\",\n \"missionModeTab\": \"Loopテンプレート - Mission\",\n \"goalModeInfoTitle\": \"Goalモード vs デフォルト\",\n \"goalModeInfo\": \"デフォルトモードは応答後に停止します。Goalモードはエージェントを目標に向かってループさせ、Rubric評価で完了を判断します。すべての操作は現在のエージェントコンテキスト内で実行されます。\",\n \"missionModeInfoTitle\": \"Missionモード vs Goalモード\",\n \"missionModeInfo\": \"Missionモードは複雑なタスクを自動分解し、独立したWorkerサブエージェントが実行、Verifierサブエージェントが検証します。各サブタスクは独自のコンテキストで実行され、メインセッションのコンテキスト汚染を防ぎます。\",\n \"goalIterationGate\": \"目標イテレーションゲート\",\n \"goalIterationGateDesc\": \"目標セッション内のエージェントターン数を制限し、イテレーション数とトークン使用量を追跡します。\",\n \"goalBudgetGate\": \"トークン予算ゲート\",\n \"goalBudgetGateDesc\": \"目標セッションのトークン消費上限を設定し、予算超過時にエージェントを停止します。\",\n \"goalRubricGate\": \"目標完了ルーブリック\",\n \"goalRubricGateDesc\": \"セッション状態をチェックして目標が達成されたかを評価します。\",\n \"goalDoomGate\": \"繰り返し保護\",\n \"goalDoomGateDesc\": \"目標実行中の繰り返しパターンを検出し、介入をトリガーします。\",\n \"missionProgressGate\": \"ミッション進捗ゲート\",\n \"missionProgressGateDesc\": \"PRDユーザーストーリーの完了状況を追跡します。すべて合格または最大イテレーションに達するまで続行します。\",\n \"missionIterBypass\": \"イテレーションバイパス\",\n \"missionIterBypassDesc\": \"ミッション実行中にReActイテレーション制限を一時的に解除し、長時間のフェーズを許可します。\",\n \"customLoopTitle\": \"カスタムループモード\",\n \"customLoopDesc\": \"カスタムゲート組み合わせで独自のループモードを作成。近日公開予定。\",\n \"autoContinueOnTextOnly\": \"テキストのみのステップで自動継続\",\n \"autoContinueOnTextOnlyTooltip\": \"推論ステップがツール呼び出しなしのテキストのみで終わった場合、ヒントを挿入し同じツール方針でもう1回推論します。多段階タスクの途中停止を減らします。オフにすると従来どおりです。有効時はトークン消費が増える場合があります。\",\n \"autoGenerateSessionTitle\": \"セッションタイトルを自動生成\",\n \"autoGenerateSessionTitleTooltip\": \"新しいチャットの最初のユーザーメッセージ後、バックグラウンドで短いLLM呼び出しを実行し、切り詰められたプレースホルダを簡潔なタイトルに置き換えます。新しいチャットごとに短いLLM呼び出しが1回追加されます。オフにするとプレースホルダのままで、その追加コストは発生しません。\",\n \"shellCommandTimeout\": \"Shellコマンドタイムアウト(秒)\",\n \"shellCommandTimeoutTooltip\": \"execute_shell_commandツールのデフォルトタイムアウト(秒)。LLMはtimeoutパラメータで呼び出しごとにこの値を上書きできます。コンパイルやデータ処理などの長時間タスクに便利です。\",\n \"shellCommandTimeoutPlaceholder\": \"タイムアウトを入力\",\n \"shellCommandTimeoutRequired\": \"Shellコマンドタイムアウトは必須です\",\n \"shellCommandTimeoutMin\": \"Shellコマンドタイムアウトは1秒以上である必要があります\",\n \"shellCommandExecutable\": \"Shell実行ファイル\",\n \"shellCommandExecutableTooltip\": \"execute_shell_commandが使用するシェルのパス。Linux/macOS: /bin/bash、/bin/zshなど。Windows: powershell.exe、pwsh.exe、またはGit Bashなどの POSIX互換シェルに対応。空の場合は $SHELL 環境変数、次にプラットフォームデフォルト(Unixは /bin/sh、Windowsは cmd.exe)にフォールバックします。\",\n \"shellCommandExecutablePlaceholder\": \"例: /bin/bash または powershell.exe(空 = 自動検出)\",\n \"maxInputLength\": \"最大入力長\",\n \"maxInputLengthTooltip\": \"モデルのコンテキストウィンドウの最大入力長(トークン数)\",\n \"maxInputLengthPlaceholder\": \"最大入力長を入力\",\n \"maxInputLengthRequired\": \"最大入力長は必須です\",\n \"maxInputLengthMin\": \"最大入力長は1000以上である必要があります\",\n \"contextCompactRatio\": \"コンテキスト圧縮比率\",\n \"contextCompactRatioTooltip\": \"コンテキストがいっぱいの時に圧縮するコンテキストの比率\",\n \"contextCompactRatioRequired\": \"コンテキスト圧縮比率は必須です\",\n \"contextCompactThreshold\": \"コンテキスト圧縮閾値\",\n \"contextCompactThresholdTooltip\": \"コンテキスト圧縮閾値(トークン数)、最大入力長と圧縮比率から自動計算\",\n \"contextCompactThresholdPlaceholder\": \"自動計算\",\n \"contextCompactReserveRatio\": \"コンテキスト予約比率\",\n \"contextCompactReserveRatioTooltip\": \"コンテキスト圧縮時に予約する比率\",\n \"contextCompactReserveRatioRequired\": \"コンテキスト予約比率は必須です\",\n \"contextCompactReserveThreshold\": \"コンテキスト予約閾値\",\n \"contextCompactReserveThresholdTooltip\": \"コンテキスト予約閾値(トークン数)、最大入力長と予約比率から自動計算\",\n \"contextCompactReserveThresholdPlaceholder\": \"自動計算\",\n \"toolResultCompactRecentN\": \"ツール呼び出し結果のrecent_n\",\n \"toolResultCompactRecentNTooltip\": \"最近のしきい値を使用するツール呼び出し結果の数\",\n \"toolResultCompactRecentNRequired\": \"ツール呼び出し結果のrecent_nは必須です\",\n \"toolResultCompactOldThreshold\": \"recent_nを超えるツール呼び出し結果の最大文字数\",\n \"toolResultCompactOldThresholdTooltip\": \"recent_n範囲外のツール呼び出し結果で、この文字数を超えると圧縮されます\",\n \"toolResultCompactOldThresholdPlaceholder\": \"文字数しきい値を入力\",\n \"toolResultCompactOldThresholdRequired\": \"recent_nを超えるツール呼び出し結果の最大文字数は必須です\",\n \"toolResultCompactRecentThreshold\": \"recent_n内のツール呼び出し結果の最大文字数\",\n \"toolResultCompactRecentThresholdTooltip\": \"recent_n範囲内のツール呼び出し結果で、このバイト数を超えると圧縮されます。また、read_fileツールの1回の読み取りで返される最大バイト数にもなります。古いメッセージのしきい値以上である必要があります\",\n \"toolResultCompactRecentThresholdPlaceholder\": \"文字数しきい値を入力\",\n \"toolResultCompactRecentThresholdRequired\": \"recent_n内のツール呼び出し結果の最大文字数は必須です\",\n \"toolResultCompactRetentionDays\": \"ツール呼び出しファイルの保持日数\",\n \"toolResultCompactRetentionDaysTooltip\": \"圧縮されたツール結果ファイルの保持日数、期限切れは自動削除されます\",\n \"toolResultCompactRetentionDaysRequired\": \"ツール呼び出しファイルの保持日数は必須です\",\n \"historyRetentionDays\": \"履歴の保持日数\",\n \"historyRetentionDaysTooltip\": \"永続的なチャット履歴を保持する日数。古い行は起動時とエージェント終了時に自動的に削除されます。\",\n \"historyRetentionDaysRequired\": \"履歴の保持日数は必須です\",\n \"historyRetentionDaysForeverWarning\": \"0 に設定すると履歴を永久に保持し、ストレージが無制限に増加します。\",\n \"historyRetentionDaysLargeWarning\": \"保持日数が大きいと、ストレージを大量に消費する可能性があります。\",\n \"exemptFileExtensions\": \"除外ファイル拡張子\",\n \"exemptFileExtensionsTooltip\": \"read_fileツールの結果で、これらのファイル拡張子には最新メッセージの圧縮しきい値が使用されます\",\n \"exemptFileExtensionsPlaceholder\": \".md, .txt, .json\",\n \"exemptToolNames\": \"除外ツール名\",\n \"exemptToolNamesTooltip\": \"これらのツールの結果には最新メッセージの圧縮しきい値が使用されます\",\n \"exemptToolNamesPlaceholder\": \"chat_with_agent, list_agents\",\n \"language\": \"エージェント言語\",\n \"languageTooltip\": \"エージェントのペルソナファイル(SOUL.md、AGENTS.md など)の言語。変更すると選択した言語のMDファイルが再コピーされます。\",\n \"languageConfirmTitle\": \"エージェント言語の変更\",\n \"languageConfirmContent\": \"言語を変更すると、以下のファイルが新しい言語のデフォルト版で上書きされます:\\n\\n SOUL.md、AGENTS.md、PROFILE.md、MEMORY.md、BOOTSTRAP.md、HEARTBEAT.md\\n\\nカスタマイズ済みのファイルがある場合は、事前にバックアップしてください。その他のファイルは影響を受けません。\",\n \"languageConfirmOk\": \"言語を変更\",\n \"languageSaveSuccess\": \"言語を更新しました\",\n \"languageSaveSuccessWithFiles\": \"言語を更新し、{{count}} 個のMDファイルをコピーしました\",\n \"languageSaveFailed\": \"言語の更新に失敗しました\",\n \"timezone\": \"ユーザータイムゾーン\",\n \"timezoneTooltip\": \"スケジュールタスク、時刻表示、エージェントコンテキストに使用されます。デフォルトはシステムのタイムゾーンです。\",\n \"timezoneRequired\": \"タイムゾーンを選択してください\",\n \"selectTimezone\": \"タイムゾーンを選択\",\n \"timezoneSaveSuccess\": \"タイムゾーンを更新しました\",\n \"timezoneSaveFailed\": \"タイムゾーンの更新に失敗しました\",\n \"saveSuccess\": \"設定を保存しました\",\n \"saveFailed\": \"設定の保存に失敗しました\",\n \"loadFailed\": \"設定の読み込みに失敗しました\",\n \"historyMaxLength\": \"履歴の最大長\",\n \"historyMaxLengthTooltip\": \"/history コマンドの最大出力長(文字数)\",\n \"historyMaxLengthRequired\": \"履歴の最大長は必須です\",\n \"historyMaxLengthMin\": \"履歴の最大長は1000以上である必要があります\",\n \"historyMaxLengthPlaceholder\": \"最大長を入力\",\n \"contextCompactTitle\": \"コンテキスト圧縮設定\",\n \"contextCompactEnabled\": \"コンテキスト圧縮を有効化\",\n \"contextCompactEnabledTooltip\": \"コンテキストウィンドウのオーバーフローを防ぐために自動的にコンテキストを圧縮します\",\n \"tokenCountModel\": \"トークンカウントモデル\",\n \"tokenCountModelTooltip\": \"精確なトークン数計算に使用するモデル名。「default」を設定すると組み込みの推定値を使用します\",\n \"tokenCountModelPlaceholder\": \"モデル名または default\",\n \"tokenCountUseMirror\": \"HuggingFaceミラーを使用\",\n \"tokenCountUseMirrorTooltip\": \"トークナイザーのダウンロード時にHuggingFaceミラーサイトを使用します\",\n \"tokenCountEstimateDivisor\": \"byte/Token 推定除数\",\n \"tokenCountEstimateDivisorTooltip\": \"バイト数に基づくトークン数推定の除数(トークン数 ≈ バイト数 / 除数)。推奨値:3.75~4\",\n \"tokenCountEstimateDivisorRequired\": \"推定除数は必須です\",\n \"tokenCountEstimateDivisorMin\": \"推定除数は2から5の範囲である必要があります\",\n \"toolResultCompactTitle\": \"ツール結果圧縮設定\",\n \"toolResultCompactEnabled\": \"ツール結果圧縮を有効化\",\n \"toolResultCompactEnabledTooltip\": \"過大なツール呼び出し結果を圧縮してコンテキストスペースを節約します\",\n \"memorySummaryTitle\": \"長期記憶設定\",\n \"memorySummaryEnabled\": \"記憶まとめを起動\",\n \"memorySummaryEnabledTooltip\": \"コンテキスト圧縮時に自動的に記憶サマリーを生成・保存します\",\n \"dreamCronEnabled\": \"ドリームを有効化\",\n \"dreamCronEnabledTooltip\": \"Cron によるドリームベースの記憶最適化タスクを有効にします\",\n \"dreamCron\": \"ドリームスケジュール\",\n \"dreamCronTooltip\": \"ドリーム状態を通じて定期的に長期記憶を最適化するCron式です。実行は上のスイッチで切り替えます\",\n \"dreamCronPlaceholder\": \"0 23 * * *\",\n \"dreamCronRequired\": \"ドリームを有効にする場合は Cron 式を入力してください\",\n \"dreamCronInvalid\": \"有効な5フィールドの Cron 式を入力してください\",\n \"autoMemorySearch\": \"自動記憶検索(Beta)\",\n \"autoMemorySearchTooltip\": \"毎回の会話で記憶検索を自動実行します。各クエリで自動的に記憶検索機能を呼び出し、QwenPawがあなたをより理解できますが、トークン消費が増加します。\",\n \"autoMaxResults\": \"自動検索の最大結果数\",\n \"autoMaxResultsTooltip\": \"自動記憶検索が有効な場合に返される最大結果数\",\n \"autoMaxResultsRequired\": \"最大結果数は必須です\",\n \"autoMaxResultsMin\": \"最大結果数は1以上である必要があります\",\n \"inboxPushEnabled\": \"記憶結果の受信トレイ通知\",\n \"inboxPushEnabledTooltip\": \"自動記憶、自動夢想、自動リソースの結果を受信トレイに送信します。無効にしても記憶ジョブは停止しません。\",\n \"autoMemoryInterval\": \"自動記憶の間隔\",\n \"autoMemoryIntervalTooltip\": \"ユーザークエリ N 回ごとに自動記憶を実行します。0 は定期的な自動記憶を無効にします。1 は各クエリ後に自動記憶を実行します。推奨設定は 2-5 です。1 に設定する場合は高いトークン消費に注意してください。\",\n \"autoMemoryIntervalPlaceholder\": \"0 を入力すると定期的な自動記憶を無効化\",\n \"autoMemoryIntervalRequired\": \"自動記憶の間隔を入力してください\",\n \"autoMemoryIntervalMin\": \"自動記憶の間隔は 0 以上である必要があります\",\n \"embeddingConfigTitle\": \"埋め込みモデル設定\",\n \"embeddingBackend\": \"埋め込みSDKタイプ\",\n \"embeddingBackendTooltip\": \"埋め込みモデルの呼び出しに使用するSDKタイプ\",\n \"embeddingBackendPlaceholder\": \"SDKタイプを選択\",\n \"embeddingApiKey\": \"API Key\",\n \"embeddingApiKeyTooltip\": \"埋め込みプロバイダーのAPIキー。認証が不要な場合は空欄にしてください\",\n \"embeddingApiKeyPlaceholder\": \"不要な場合は空欄\",\n \"embeddingBaseUrl\": \"Base URL\",\n \"embeddingBaseUrlTooltip\": \"埋め込みAPIのBase URL。OpenAI互換バックエンドでは、このカスタムエンドポイントは任意で、API Keyとモデル名が必須です\",\n \"embeddingBaseUrlPlaceholder\": \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n \"embeddingHost\": \"Host\",\n \"embeddingHostTooltip\": \"OllamaサービスのURL。空欄の場合はOllamaのデフォルトホストを使用します\",\n \"embeddingHostPlaceholder\": \"http://localhost:11434\",\n \"embeddingModelName\": \"モデル名\",\n \"embeddingModelNameTooltip\": \"埋め込みモデル名。すべての埋め込みバックエンドで必須です\",\n \"embeddingModelNamePlaceholder\": \"text-embedding-v4\",\n \"embeddingEnableCache\": \"埋め込みキャッシュを有効化\",\n \"embeddingEnableCacheTooltip\": \"埋め込み結果をキャッシュして重複計算を削減します\",\n \"embeddingMaxCacheSize\": \"最大キャッシュサイズ\",\n \"embeddingMaxCacheSizeTooltip\": \"埋め込みキャッシュの最大エントリ数\",\n \"embeddingMaxCacheSizeRequired\": \"最大キャッシュサイズは必須です\",\n \"embeddingUseDimensions\": \"カスタム次元を使用\",\n \"embeddingUseDimensionsTooltip\": \"カスタム埋め込み次元を使用します(一部のモデルのみ対応)\",\n \"embeddingDimensions\": \"埋め込み次元数\",\n \"embeddingDimensionsTooltip\": \"埋め込みの次元数。カスタム次元が有効な場合に適用されます\",\n \"embeddingDimensionsRequired\": \"埋め込み次元数は必須です\",\n \"embeddingDimensionsMin\": \"次元数は1以上である必要があります\",\n \"embeddingMaxInputLength\": \"最大入力長\",\n \"embeddingMaxInputLengthTooltip\": \"単一の埋め込みリクエストの最大入力長(トークン数)\",\n \"embeddingMaxInputLengthRequired\": \"最大入力長は必須です\",\n \"embeddingMaxBatchSize\": \"最大バッチサイズ\",\n \"embeddingMaxBatchSizeTooltip\": \"単一バッチ埋め込みリクエストの最大アイテム数\",\n \"embeddingMaxBatchSizeRequired\": \"最大バッチサイズは必須です\",\n \"embeddingRestartWarning\": \"埋め込みモデル設定はホットリロードに対応していません。変更を反映するには保存後にサービスを再起動してください\",\n \"maxContextLength\": \"最大コンテキスト長\",\n \"maxContextLengthTooltip\": \"モデルコンテキストウィンドウの最大トークン数。各リクエストで保持できる最大履歴長を決定します\",\n \"maxContextLengthRequired\": \"最大コンテキスト長は必須です\",\n \"maxContextLengthMin\": \"最大コンテキスト長は1000以上である必要があります\",\n \"maxContextLengthPlaceholder\": \"最大コンテキスト長を入力\",\n \"embeddingActivationNote\": \"選択したバックエンドの必須項目が入力されると埋め込み機能が有効になります\",\n \"embeddingEnableHint\": \"選択したバックエンドの必須項目が入力されると埋め込み機能が有効になります。OpenAI互換またはGeminiではモデル名とAPI Key、Ollamaではモデル名が必要です。\",\n \"rebuildMemoryIndex\": \"記憶インデックスを再構築\",\n \"rebuildMemoryIndexWarning\": \"この操作は記憶検索インデックスを消去して再構築するため、多くのCPUとメモリを消費する可能性があります。インデックスの破損や検索結果の異常を修復する場合にのみ使用してください。\",\n \"rebuildMemoryIndexConfirmTitle\": \"記憶インデックスを再構築しますか?\",\n \"rebuildMemoryIndexConfirm\": \"再構築中はCPUとメモリの使用量が大幅に増加する可能性があります。インデックスが破損している、または修復が必要な場合を除き、実行は推奨されません。\",\n \"rebuildMemoryIndexSuccess\": \"記憶インデックスの再構築が完了しました\",\n \"rebuildMemoryIndexFailed\": \"記憶インデックスの再構築に失敗しました:{{error}}\",\n \"toolResultCompactRecentGtOld\": \"最新メッセージの閾値は古いメッセージの閾値以上である必要があります\",\n \"lightContextTitle\": \"コンテキスト管理\",\n \"visualCompactCollapseLabel\": \"ビジュアル圧縮\",\n \"visualCompactDescription\": \"古く長いコンテキストをビジュアルページに圧縮して入力トークンを削減します。最近の会話はテキストのまま保持されます。\",\n \"visualCompactEnabled\": \"Visual Compact を有効にする\",\n \"visualCompactEffort\": \"圧縮強度\",\n \"visualCompactLow\": \"低\",\n \"visualCompactMedium\": \"中\",\n \"visualCompactHigh\": \"高\",\n \"visualCompactLowDescription\": \"圧縮する内容を抑え、読みやすさを優先します。\",\n \"visualCompactMediumDescription\": \"トークン削減と視認性のバランスを取ります。\",\n \"visualCompactHighDescription\": \"対象コンテキストをより多く圧縮し、トークン削減を優先します。\",\n \"visualCompactQualityNote\": \"圧縮強度を上げても、回答品質が向上するとは限りません。\",\n \"visualCompactCapabilityNote\": \"画像入力に対応するモデルでのみ有効です。\",\n \"remeLightMemoryTitle\": \"長期記憶\",\n \"toolExecutionLevelTitle\": \"ツール実行セキュリティ\",\n \"toolExecutionLevel\": {\n \"title\": \"ツール実行セキュリティ\",\n \"alertMessage\": \"ツール呼び出しの承認ポリシーを設定し、エージェントがツールを実行する際のセキュリティレベルを制御します\",\n \"strict\": \"厳格モード\",\n \"strictDesc\": \"すべてのツール呼び出しに承認が必要、最高セキュリティレベル\",\n \"smart\": \"スマートモード\",\n \"smartDesc\": \"低リスクツールは自動承認、中〜高リスクツールは承認が必要\",\n \"auto\": \"自動モード\",\n \"autoDesc\": \"承認が必要と明示的にマークされたツールのみ承認を要求(デフォルト)\",\n \"off\": \"オフモード\",\n \"offDesc\": \"すべてのツール承認を無効化、すべてのツールが自動実行\"\n },\n \"saveLevelSuccess\": \"ツール実行セキュリティレベルを保存しました\",\n \"saveLevelFailed\": \"ツール実行セキュリティレベルの保存に失敗しました\",\n \"adbpgMemoryTitle\": \"ADBPG Long-term Memory\",\n \"adbpgConfig\": {\n \"title\": \"ADBPG Memory Configuration\",\n \"restBaseUrl\": \"REST Base URL\",\n \"restApiKey\": \"REST API Key\",\n \"memoryIsolation\": \"Memory Isolation (per-agent)\",\n \"searchTimeout\": \"Search Timeout\"\n }\n },\n \"tools\": {\n \"title\": \"ビルトインツール\",\n \"description\": \"ビルトインツールとその有効状態を管理します。無効なツールはエージェントで使用できません。\",\n \"emptyState\": \"ツールが設定されていません\",\n \"loadError\": \"ツールの読み込みに失敗しました\",\n \"enableSuccess\": \"ツールを有効化しました\",\n \"disableSuccess\": \"ツールを無効化しました\",\n \"toggleError\": \"ツール状態の切り替えに失敗しました\",\n \"enableAll\": \"すべて有効化\",\n \"disableAll\": \"すべて無効化\",\n \"enableAllSuccess\": \"すべてのツールを有効化しました\",\n \"disableAllSuccess\": \"すべてのツールを無効化しました\",\n \"allEnabled\": \"すべてのツールは既に有効です\",\n \"allDisabled\": \"すべてのツールは既に無効です\",\n \"asyncExecution\": \"非同期実行\",\n \"asyncExecutionEnabled\": \"非同期実行が有効です\",\n \"asyncExecutionDisabled\": \"非同期実行が無効です\",\n \"browserUnifiedMode\": \"統合ブラウザー(ベータ)\",\n \"browserLegacyMode\": \"レガシー互換モード\",\n \"browserUnifiedModeButton\": \"新版(ベータ)\",\n \"browserLegacyModeButton\": \"旧版(互換)\",\n \"browserUnifiedDescription\": \"新しい統合ブラウザー機能を使用します。\",\n \"browserLegacyDescription\": \"従来の動作が必要な場合は、元のブラウザー実装を使用します。\",\n \"browserRestartPending\": \"サービスの再起動後に {{mode}} へ切り替わります。\",\n \"configure\": \"設定\",\n \"configured\": \"設定済み\",\n \"requiresConfig\": \"設定が必要です\",\n \"configSaved\": \"設定を保存しました\",\n \"configSaveError\": \"設定の保存に失敗しました\",\n \"active\": \"個が有効\",\n \"available\": \"利用可能\",\n \"noEnabled\": \"有効なツールはありません\",\n \"goEnableBtn\": \"有効にする\",\n \"configureAction\": \"設定 →\",\n \"enableAction\": \"有効化 →\"\n },\n \"modelConfig\": {\n \"promptTitle\": \"LLMモデルが必要です — 設定後にChatページで選択\",\n \"promptMessage\": \"チャット機能にはLLMモデルが必要です。まずモデルを設定し、設定完了後にChatページ上部のモデルセレクターで選択してください。\",\n \"configureButton\": \"モデルを設定\",\n \"skipButton\": \"スキップ\",\n \"chatDisabledTitle\": \"チャット無効\",\n \"chatDisabledMessage\": \"モデルが設定されていないためチャット機能が無効です。チャットを有効にするにはモデルを設定してください。\",\n \"configureNow\": \"モデルを設定\",\n \"modelNotConfigured\": \"チャットを使用する前に設定でモデルを設定してください\"\n },\n \"header\": {\n \"changelog\": \"変更履歴\",\n \"docs\": \"ドキュメント\",\n \"tutorial\": \"チュートリアル\",\n \"featureDemos\": \"機能デモ\",\n \"faq\": \"よくある質問\",\n \"github\": \"GitHub\",\n \"resources\": \"ドキュメント資料\",\n \"updateAvailable\": \"利用可能な更新\"\n },\n \"theme\": {\n \"dark\": \"ダーク\",\n \"light\": \"ライト\",\n \"system\": \"システムに従う\"\n },\n \"sidebar\": {\n \"newVersion\": \"新しいバージョンが利用可能です: v{{version}}、クリックしてアップグレード\",\n \"updateModal\": {\n \"title\": \"新しいバージョンが利用可能です: v{{version}}\",\n \"viewReleases\": \"リリースを見る\",\n \"installDesktopUpdate\": \"今すぐ更新\",\n \"desktopInstallHint\": \"QwenPaw Desktop {{version}} のインストール準備が整いました。更新後にアプリが再起動します。\",\n \"checking\": \"更新を確認中…\",\n \"checkingHint\": \"最新バージョンを確認しています\",\n \"downloading\": \"更新をダウンロード中\",\n \"downloadingTo\": \"バージョン {{version}} に更新中\",\n \"downloadProgress\": \"{{done}} / {{total}} · {{rate}}\",\n \"installing\": \"更新をインストール中\",\n \"willRestart\": \"アプリはインストール完了後に自動的に再起動します\",\n \"stepPrepare\": \"準備\",\n \"stepDownloading\": \"ダウンロード\",\n \"stepInstalling\": \"インストール\",\n \"failedTitle\": \"更新に失敗しました\",\n \"back\": \"戻る\",\n \"retry\": \"再試行\",\n \"errors\": {\n \"network\": \"更新サーバーに接続できませんでした。インターネット接続を確認して再試行してください。\",\n \"signature\": \"更新ファイルの署名検証に失敗しました。もう一度ダウンロードしてください。\",\n \"appLocation\": \"QwenPaw は読み取り専用の macOS の場所から実行されています。QwenPaw.app を Applications フォルダーへ移動し、そこから起動してからもう一度更新してください。\",\n \"other\": \"更新に失敗しました。\"\n },\n \"updateLater\": \"バックグラウンドでダウンロード\",\n \"backgroundDownloading\": \"バックグラウンドでダウンロード中…\",\n \"readyToInstall\": \"更新の準備が完了\",\n \"readyToInstallHint\": \"v{{version}} の更新をダウンロードしました。再起動してインストールし、新しいバージョンを起動します。\",\n \"restartNow\": \"今すぐ更新\",\n \"backgroundFailed\": \"バックグラウンドダウンロード失敗\"\n },\n \"settings\": {\n \"language\": \"言語\",\n \"theme\": \"テーマ\",\n \"mode\": \"モード\"\n },\n \"simpleMode\": \"シンプルモード\",\n \"fullMode\": \"フルモード\"\n },\n \"chat\": {\n \"modelSelectTooltip\": \"モデルを選択\",\n \"newChatTooltip\": \"新しいチャット\",\n \"searchTooltip\": \"すべてのチャットを検索\",\n \"chatHistoryTooltip\": \"チャット履歴\",\n \"wideModeTooltip\": \"ワイドモード\",\n \"normalModeTooltip\": \"標準モード\",\n \"allChats\": \"すべてのチャット\",\n \"createNewChat\": \"新しいチャットを作成\",\n \"pinDrawer\": \"固定\",\n \"unpinDrawer\": \"固定解除\",\n \"statusIdle\": \"待機\",\n \"statusInProgress\": \"処理中\",\n \"disclaimer\": \"あなたのために働き、あなたと共に成長します\",\n \"greeting\": \"こんにちは、今日はどんなお手伝いができますか?\",\n \"description\": \"私はあなたの質問をサポートするAIアシスタントです。\",\n \"prompt1\": \"新しい旅を始めましょう!\",\n \"prompt2\": \"あなたのスキルを教えてください。\",\n \"inputPlaceholder\": \"\\\"↑↓\\\"でメッセージ移動 · \\\"/\\\"でクイックコマンド(承認時は\\\"/approve\\\"または\\\"/deny\\\")\",\n \"longTextUploadPrompt\": \"アップロードされたプロンプトファイルを読み、その内容に基づいて回答してください。\",\n \"emptyOutputError\": \"アシスタントが空のレスポンスを返しました。\",\n \"commands\": {\n \"clear\": {\n \"description\": \"会話のコンテキストをクリア\"\n },\n \"compact\": {\n \"description\": \"会話コンテキストを圧縮して要約を生成。追加指示にも対応\"\n },\n \"approve\": {\n \"description\": \"保留中のツール操作を承認して実行を続行\"\n },\n \"deny\": {\n \"description\": \"保留中のツール操作を拒否\"\n },\n \"mission\": {\n \"description\": \"Missionモード — タスクをサブエージェントに分解委任、独立コンテキストでメイン会話を汚染しない\"\n },\n \"skills\": {\n \"description\": \"チャットで利用可能なスキルを一覧し、明示的なスキルコマンドを表示します\"\n },\n \"agents\": {\n \"description\": \"現在のランタイムで利用可能なエージェントを一覧表示\"\n },\n \"review\": {\n \"description\": \"Codex で未コミットのワークスペース変更をレビュー\"\n },\n \"status\": {\n \"description\": \"現在のエージェントアカウント、モデル、ワークスペースを表示\"\n },\n \"goal\": {\n \"description\": \"Goalループ — エージェントが目標に向かって継続実行し、Rubric評価で完了を自動判定\"\n }\n },\n \"attachments\": {\n \"tooltip\": \"ドキュメント、画像、動画、音声など、様々な形式に対応\",\n \"tooltipImageOnly\": \"現在のモデルは画像アップロードのみ対応\",\n \"tooltipNoMultimodal\": \"現在のモデルにマルチモーダル機能が検出されていません。画像や動画が正しく処理されない場合があります\",\n \"fileSizeLimit\": \"1ファイルあたり最大{{limit}}MB\",\n \"fileSizeExceeded\": \"ファイルサイズが{{limit}}MBの制限を超えています。現在のファイル: {{size}}MB\",\n \"multimodalDisabled\": \"現在のモデルはファイルアップロードに対応していません\",\n \"multimodalWarning\": \"現在のモデルにマルチモーダル機能が検出されていません。画像や動画が正しく処理されない場合があります\",\n \"imageOnlyAccept\": \"現在のモデルは画像ファイルのみ対応しています\",\n \"imageOnlyWarning\": \"現在のモデルは画像のみ対応しています。動画などの非画像ファイルは正しく処理されない場合があります\"\n },\n \"rateLimitHit\": \"現在のモデルがレート制限されています。他の無料モデルに切り替えてみてください。\",\n \"turnUsagePopover\": {\n \"ariaLabel\": \"このターンのトークンとコンテキスト使用量を表示\",\n \"turn\": \"このターン\",\n \"turnEstimated\": \"このターン約\",\n \"tok\": \"tok\",\n \"inOut\": \"入力 {{inTok}} · 出力 {{outTok}}\",\n \"contextLabel\": \"コンテキストウィンドウ:\",\n \"manageContext\": \"コンテキスト管理\",\n \"compact\": \"圧縮\",\n \"new\": \"新しい会話\"\n },\n \"rateLimitMessage\": \"レート制限に達しました。他の無料モデルに切り替え:\",\n \"rateLimitSwitched\": \"{{model}} に切り替えました\",\n \"speech\": {\n \"startRecording\": \"音声録音を開始 (Ctrl+Shift+M)\",\n \"stopRecording\": \"録音を停止 (Ctrl+Shift+M)\",\n \"transcribing\": \"文字起こし中...\",\n \"transcriptionDisabled\": \"文字起こしは無効です。設定 > 音声文字起こしでプロバイダーを設定してください。\",\n \"transcriptionFailed\": \"音声文字起こしに失敗しました\",\n \"microphoneError\": \"マイクにアクセスできません\",\n \"fileTooLarge\": \"録音ファイルが大きすぎます({{size}}MB)。上限: {{limit}}MB\",\n \"recordingTooLong\": \"録音時間が制限を超えています(上限: {{limit}}秒)\"\n },\n \"search\": {\n \"title\": \"チャットを検索\",\n \"tooltip\": \"現在のチャットを検索\",\n \"placeholder\": \"メッセージを検索...\",\n \"noResults\": \"結果が見つかりません\",\n \"resultsCount\": \"{{count}} 件の結果が見つかりました\",\n \"searching\": \"検索中 {{progress}}...\",\n \"loading\": \"読み込み中...\",\n \"userMessage\": \"ユーザー\",\n \"assistantMessage\": \"アシスタント\",\n \"titleMatch\": \"タイトル\"\n },\n \"contextMenu\": {\n \"open\": \"開く\",\n \"rename\": \"名前を変更\",\n \"pin\": \"ピン留め\",\n \"unpin\": \"ピン留め解除\",\n \"delete\": \"削除\"\n },\n \"queue\": {\n \"title\": \"メッセージキュー\",\n \"paused\": \"一時停止中\",\n \"sendFailed\": \"送信失敗\",\n \"resume\": \"キューを再開\",\n \"pause\": \"キューを一時停止\",\n \"clear\": \"キューをクリア\",\n \"dragSort\": \"ドラッグで並べ替え\",\n \"edit\": \"編集\",\n \"retry\": \"再試行\",\n \"skip\": \"スキップ\",\n \"interruptAndSend\": \"中断して送信\",\n \"delete\": \"削除\",\n \"file\": \"ファイル\",\n \"noChatWarning\": \"キューを使用する前に、まずメッセージを送信して会話を作成してください\",\n \"queueFull\": \"キューが満杯です(最大 {{max}} 件)。メッセージを送信または削除してください\",\n \"otherTabOwner\": \"This tab queues only; sending is handled by another tab\"\n },\n \"group\": {\n \"pinned\": \"ピン留め\",\n \"today\": \"今日\",\n \"week\": \"7日以内\",\n \"month\": \"30日以内\",\n \"older\": \"それ以前\"\n },\n \"sessionPanel\": {\n \"searchConversations\": \"会話を検索…\",\n \"noResults\": \"結果なし\",\n \"noConversations\": \"会話なし\",\n \"loadingConversations\": \"読み込み中…\"\n },\n \"conversationHistory\": \"会話履歴\",\n \"history\": {\n \"empty\": \"チャット履歴なし\"\n },\n \"sessionGroup\": {\n \"today\": \"今日\",\n \"recent\": \"過去7日間\",\n \"earlier\": \"それ以前\"\n }\n },\n \"security\": {\n \"parent\": \"設定\",\n \"security\": \"セキュリティ\",\n \"title\": \"セキュリティ\",\n \"description\": \"ツールとスキルのセキュリティ機能を管理します。\",\n \"toolGuardTitle\": \"ツールガード\",\n \"toolGuardDescription\": \"ツール呼び出しのセキュリティスキャンを設定します。危険な操作は実行前に明示的な承認が必要になります。\",\n \"enabled\": \"ツールガードを有効にする\",\n \"enabledTooltip\": \"有効にすると、ツール呼び出しが実行前に危険なパターンをスキャンされます\",\n \"guardedTools\": \"保護対象ツール\",\n \"guardedToolsTooltip\": \"危険なパターンが検出された場合に承認が必要なツール。空の場合はデフォルトセットが使用されます。\",\n \"guardedToolsPlaceholder\": \"ツールを選択またはカスタムツール名を入力\",\n \"deniedTools\": \"拒否ツール\",\n \"deniedToolsTooltip\": \"承認なしで常に拒否されるツール\",\n \"deniedToolsPlaceholder\": \"常に拒否するツールを選択\",\n \"saveSuccess\": \"ツールガード設定を保存しました\",\n \"saveFailed\": \"ツールガード設定の保存に失敗しました\",\n \"loadFailed\": \"ツールガード設定の読み込みに失敗しました\",\n \"rules\": {\n \"title\": \"検出ルール\",\n \"description\": \"ルールはセキュリティアラートをトリガーする正規表現パターンを定義します。組み込みルールは無効化でき、カスタムルールは追加・編集・削除できます。\",\n \"id\": \"ルール ID\",\n \"severity\": \"重要度\",\n \"descriptionCol\": \"説明\",\n \"source\": \"ソース\",\n \"actions\": \"操作\",\n \"builtin\": \"組み込み\",\n \"custom\": \"カスタム\",\n \"enable\": \"有効化\",\n \"disable\": \"無効化\",\n \"edit\": \"編集\",\n \"delete\": \"削除\",\n \"add\": \"ルール追加\",\n \"addTitle\": \"カスタムルールを追加\",\n \"editTitle\": \"カスタムルールを編集\",\n \"ruleId\": \"ルール ID\",\n \"ruleIdRequired\": \"ルール ID は必須です\",\n \"duplicateId\": \"このIDのルールは既に存在します\",\n \"tools\": \"対象ツール\",\n \"toolsPlaceholder\": \"空白の場合はすべてのツールに一致\",\n \"params\": \"対象パラメータ\",\n \"paramsPlaceholder\": \"空白の場合はすべてのパラメータに一致\",\n \"severityLabel\": \"重要度\",\n \"categoryLabel\": \"カテゴリ\",\n \"patterns\": \"正規表現パターン\",\n \"patternsRequired\": \"パターンが少なくとも1つ必要です\",\n \"patternsTooltip\": \"1行に1つの正規表現。ツールパラメータ値に対してマッチング(大文字小文字区別なし)。\",\n \"excludePatterns\": \"除外パターン\",\n \"excludePatternsTooltip\": \"1行に1つの正規表現。マッチした場合、そのルールはスキップされます。\",\n \"descriptionLabel\": \"説明\",\n \"descriptionPlaceholder\": \"このルールは何を検出しますか?\",\n \"remediationLabel\": \"修正提案\",\n \"remediationPlaceholder\": \"ルールがトリガーされた場合の推奨アクション\",\n \"preview\": \"プレビュー\",\n \"previewTitle\": \"ルール詳細\",\n \"actionLabel\": \"アクション\",\n \"actionApproval\": \"承認待ち\",\n \"autoDeny\": \"自動拒否\",\n \"autoDenyTooltip\": \"有効にすると、このルールに一致するツール呼び出しは手動承認なしで自動的に拒否されます\",\n \"autoDenyEnable\": \"このルールの自動拒否を有効にする\",\n \"autoDenyDisable\": \"このルールの自動拒否を無効にする\",\n \"allTools\": \"すべてのツール\",\n \"allParams\": \"すべてのパラメータ\",\n \"descriptions\": {\n \"TOOL_CMD_CONTROL_CHARS\": \"セキュリティチェックをバイパスする可能性のある非印刷制御文字を含むコマンドを検出\",\n \"TOOL_CMD_DANGEROUS_MV\": \"ファイルを意図せず移動・上書きする可能性のある mv コマンドを検出\",\n \"TOOL_CMD_DANGEROUS_RM\": \"データ損失を引き起こす可能性のある rm コマンドを検出\",\n \"TOOL_CMD_DOS_FORK_BOMB\": \"Bash フォーク爆弾および大量プロセス終了を検出\",\n \"TOOL_CMD_FIND_DELETE\": \"データ損失を引き起こす可能性のある find -delete コマンドを検出\",\n \"TOOL_CMD_FS_DESTRUCTION\": \"低レベルのディスクフォーマットまたはワイプコマンドを検出\",\n \"TOOL_CMD_IFS_INJECTION\": \"$IFS変数を使用してセキュリティ検証をバイパスする可能性のあるコマンドを検出\",\n \"TOOL_CMD_JQ_FILE_FLAGS\": \"任意のファイルを読み取ったり外部コードを実行する可能性のあるフラグを使用するjqコマンドを検出\",\n \"TOOL_CMD_JQ_SYSTEM\": \"任意のシェルコマンドを実行できるsystem()関数を含むjqコマンドを検出\",\n \"TOOL_CMD_OBFUSCATED_EXEC\": \"base64 エンコード文字列をシェルインタプリタに直接渡して実行する操作を検出\",\n \"TOOL_CMD_PIPE_TO_SHELL\": \"リモートペイロードをダウンロードして即座に実行する 'curl | bash' パターンを検出\",\n \"TOOL_CMD_PRIVILEGE_ESCALATION\": \"sudo、su、doas、pkexec、または runas を使用した権限昇格の試みを検出\",\n \"TOOL_CMD_PROCESS_KILL\": \"重要なプロセスを終了させる可能性のあるプロセス終了コマンドを検出\",\n \"TOOL_CMD_PROC_ENVIRON\": \"機密の環境変数を公開する可能性のある /proc/*/environ にアクセスするコマンドを検出\",\n \"TOOL_CMD_REVERSE_SHELL\": \"リバースシェルまたは不正なネットワークトンネルの確立を検出\",\n \"TOOL_CMD_SERVICE_RESTART\": \"システムサービスを中断させる可能性のあるサービス管理コマンドを検出\",\n \"TOOL_CMD_SYSTEM_REBOOT\": \"ホストシステムを終了させるシステム再起動またはシャットダウンコマンドを検出\",\n \"TOOL_CMD_SYSTEM_TAMPERING\": \"cron ジョブ、SSH キー、sudo 権限へのアクセス(読み取り・変更を含む)を検出\",\n \"TOOL_CMD_UNICODE_WHITESPACE\": \"解析の不整合を引き起こす可能性のあるUnicode空白文字を含むコマンドを検出\",\n \"TOOL_CMD_UNSAFE_PERMISSIONS\": \"グローバル権限の引き下げ(chmod 777)や不変フラグの設定を検出\",\n \"TOOL_CMD_ZSH_DANGEROUS\": \"セキュリティチェックをバイパスする可能性のあるZsh固有のビルトインを使用するコマンドを検出\"\n },\n \"categories\": {\n \"command_injection\": \"コマンドインジェクション\",\n \"code_execution\": \"コード実行\",\n \"data_exfiltration\": \"データ漏洩\",\n \"path_traversal\": \"パストラバーサル\",\n \"sensitive_file_access\": \"機密ファイルアクセス\",\n \"network_abuse\": \"ネットワーク不正利用\",\n \"credential_exposure\": \"認証情報漏洩\",\n \"resource_abuse\": \"リソース不正利用\",\n \"privilege_escalation\": \"権限昇格\",\n \"prompt_injection\": \"プロンプトインジェクション\",\n \"other\": \"その他\"\n }\n },\n \"fileGuard\": {\n \"title\": \"ファイルガード\",\n \"description\": \"機密ファイルやディレクトリをエージェントツールからのアクセスから保護します。追加されたパスは、すべてのツール呼び出しでブロックされます。\",\n \"enableLabel\": \"ファイルガードを有効にする\",\n \"path\": \"パス\",\n \"actions\": \"操作\",\n \"add\": \"追加\",\n \"inputPlaceholder\": \"ファイルまたはディレクトリパスを入力(例: ~/.ssh/ または /etc/passwd)\",\n \"empty\": \"カスタム機密パスが設定されていません\",\n \"directory\": \"ディレクトリ\",\n \"removeConfirm\": \"このパスを保護リストから削除しますか?\",\n \"duplicate\": \"このパスは既にリストに存在します\",\n \"saveSuccess\": \"ファイルガード設定を保存しました\",\n \"saveFailed\": \"ファイルガード設定の保存に失敗しました\",\n \"loadFailed\": \"ファイルガード設定の読み込みに失敗しました\",\n \"allowPreviewOutsideWorkspace\": \"コンソールでワークスペース外ファイルのプレビューを許可\",\n \"allowPreviewOutsideWorkspaceDesc\": \"コンソールでワークスペースディレクトリ外のファイルをプレビューできるようにします。機密ファイル保護は引き続き有効です。\"\n },\n \"allowNoAuthHosts\": {\n \"title\": \"認証不要ホスト\",\n \"warningTitle\": \"セキュリティ警告\",\n \"warningDescription\": \"このリストのIPアドレスは認証なしでAPIエンドポイントにアクセスできます。デフォルトでは、ローカルホスト(127.0.0.1と::1)がCLIアクセスのために許可されています。信頼できるIPアドレスのみを追加してください。警告:信頼できないIPを追加すると、深刻なセキュリティリスクが生じます。\",\n \"ipAddress\": \"IPアドレス\",\n \"actions\": \"操作\",\n \"add\": \"追加\",\n \"inputPlaceholder\": \"IPアドレスを入力(例:192.168.1.100 または ::1)\",\n \"empty\": \"IPアドレスが設定されていません\",\n \"default\": \"デフォルト\",\n \"removeConfirm\": \"このIPアドレスをホワイトリストから削除しますか?\",\n \"duplicate\": \"このIPアドレスは既にリストに存在します\",\n \"invalidIP\": \"無効なIPアドレス形式\",\n \"saveSuccess\": \"認証不要ホスト設定を保存しました\",\n \"saveFailed\": \"認証不要ホスト設定の保存に失敗しました\",\n \"loadFailed\": \"認証不要ホスト設定の読み込みに失敗しました\"\n },\n \"skillScanner\": {\n \"title\": \"スキルスキャナー\",\n \"description\": \"スキルを有効化・インストールする前にセキュリティ脅威を自動スキャンします。\",\n \"mode\": \"スキャンモード\",\n \"modeTooltip\": \"スキャナーが安全でないスキルをどう処理するかを制御: ブロック、警告のみ、オフ\",\n \"modeBlock\": \"ブロック\",\n \"modeWarn\": \"警告のみ\",\n \"modeOff\": \"オフ\",\n \"timeout\": \"スキャンタイムアウト(秒)\",\n \"timeoutTooltip\": \"スキャン完了を待つ最大時間(5-300秒)\",\n \"saveSuccess\": \"スキルスキャナー設定を保存しました\",\n \"saveFailed\": \"スキルスキャナー設定の保存に失敗しました\",\n \"scanAlerts\": {\n \"title\": \"スキャンアラート\",\n \"empty\": \"セキュリティアラートはありません\",\n \"clearAll\": \"すべてクリア\",\n \"clearConfirm\": \"すべてのスキャンアラートをクリアしますか?\",\n \"skillName\": \"スキル\",\n \"action\": \"アクション\",\n \"actionBlocked\": \"ブロック済み\",\n \"actionWarned\": \"警告済み\",\n \"time\": \"日時\",\n \"actions\": \"操作\",\n \"allowSkill\": \"ホワイトリストに追加\",\n \"remove\": \"削除\",\n \"viewFindings\": \"詳細を表示\"\n },\n \"whitelist\": {\n \"title\": \"ホワイトリスト\",\n \"empty\": \"ホワイトリストにスキルがありません\",\n \"skillName\": \"スキル\",\n \"addedAt\": \"追加日時\",\n \"contentHash\": \"コンテンツハッシュ\",\n \"actions\": \"操作\",\n \"remove\": \"削除\",\n \"removeConfirm\": \"このスキルをホワイトリストから削除しますか?\",\n \"addSuccess\": \"スキルをホワイトリストに追加しました\",\n \"removeSuccess\": \"スキルをホワイトリストから削除しました\",\n \"addFailed\": \"ホワイトリストへの追加に失敗しました\",\n \"removeFailed\": \"ホワイトリストからの削除に失敗しました\",\n \"removeWillDisable\": \"削除後、このスキルも無効化されます。\",\n \"removeAndDisabled\": \"スキルをホワイトリストから削除し、無効化しました\"\n },\n \"scanError\": {\n \"title\": \"セキュリティ問題が検出されました\",\n \"description\": \"以下のセキュリティ問題が見つかりました:\",\n \"warnDescription\": \"セキュリティ問題が見つかりましたが、スキルは引き続き有効です(警告モード):\",\n \"goToWhitelist\": \"ホワイトリストへ\",\n \"moreFindings\": \"他に {{count}} 件は表示していません(一覧は 設定 → セキュリティ → スキルスキャナ を参照)。\"\n }\n }\n },\n \"login\": {\n \"title\": \"QwenPaw にログイン\",\n \"registerTitle\": \"アカウント作成\",\n \"firstUserHint\": \"管理者アカウントを作成して始めましょう\",\n \"usernamePlaceholder\": \"ユーザー名\",\n \"passwordPlaceholder\": \"パスワード\",\n \"usernameRequired\": \"ユーザー名を入力してください\",\n \"passwordRequired\": \"パスワードを入力してください\",\n \"submit\": \"ログイン\",\n \"register\": \"登録\",\n \"failed\": \"ログインに失敗しました。認証情報を確認してください\",\n \"registerSuccess\": \"登録が完了しました\",\n \"registerFailed\": \"登録に失敗しました\",\n \"authNotEnabled\": \"認証は有効になっていません\",\n \"logout\": \"ログアウト\",\n \"logoutSuccess\": \"正常にログアウトしました\",\n \"sessionExpired\": \"セッションが期限切れです。再度ログインしてください\",\n \"unauthorized\": \"未認証です。ログインしてください\"\n },\n \"account\": {\n \"title\": \"アカウント管理\",\n \"currentPassword\": \"現在のパスワード\",\n \"currentPasswordRequired\": \"現在のパスワードを入力してください\",\n \"newUsername\": \"新しいユーザー名\",\n \"newUsernamePlaceholder\": \"変更しない場合は空欄\",\n \"newPassword\": \"新しいパスワード\",\n \"newPasswordPlaceholder\": \"変更しない場合は空欄\",\n \"confirmPassword\": \"新しいパスワードの確認\",\n \"confirmPasswordPlaceholder\": \"新しいパスワードを再入力\",\n \"passwordMismatch\": \"パスワードが一致しません\",\n \"save\": \"変更を保存\",\n \"updateSuccess\": \"アカウントが更新されました\",\n \"updateFailed\": \"アカウントの更新に失敗しました\",\n \"wrongPassword\": \"現在のパスワードが正しくありません\",\n \"nothingToUpdate\": \"新しいユーザー名またはパスワードを入力してください\",\n \"passwordEmpty\": \"パスワードを空白にすることはできません\",\n \"usernameEmpty\": \"ユーザー名を空白にすることはできません\"\n },\n \"voiceTranscription\": {\n \"title\": \"音声文字起こし\",\n \"description\": \"受信した音声メッセージの処理方法を設定します。\",\n \"loadFailed\": \"音声モード設定の読み込みに失敗しました\",\n \"saveSuccess\": \"音声モードを保存しました\",\n \"saveFailed\": \"音声モードの保存に失敗しました\",\n \"audioModeLabel\": \"音声モード\",\n \"audioModeDescription\": \"チャンネル(Discord、Telegram など)からの音声メッセージをモデルに送信する前にどのように処理するかを選択します。\",\n \"modeAuto\": \"自動(推奨)\",\n \"modeAutoDesc\": \"選択した文字起こしプロバイダーで音声をテキストに変換してからモデルに送信します。文字起こしが無効または利用できない場合、ファイルアップロードのプレースホルダーが表示されます。このモードでは音声はモデルに直接送信されません。すべてのモデルで動作します。\",\n \"modeNative\": \"ネイティブ音声\",\n \"modeNativeDesc\": \"文字起こしせずに音声ファイルをモデルに直接送信します。音声をモデルに送信する唯一のモードです。音声対応モデル(例: gpt-4o-audio)でのみ動作します。ほとんどのモデルはこのモードをサポートしていません。\",\n \"ffmpegReady\": \"ffmpegがインストールされています。ネイティブモードの音声変換が利用可能です。\",\n \"ffmpegMissing\": \"ffmpegがインストールされていません。\",\n \"ffmpegMissingDesc\": \"ネイティブ音声モードでは、音声形式の変換(.oggから.wavなど)にffmpegが必要です。このモードを有効にするには、ffmpegをシステムパッケージとしてインストールしてください。\",\n \"providerTypeLabel\": \"文字起こしプロバイダー\",\n \"providerTypeDescription\": \"文字起こしバックエンドを選択します。音声文字起こしが不要な場合は「無効」を選択してください。\",\n \"providerTypeDisabled\": \"無効\",\n \"providerTypeDisabledDesc\": \"文字起こしなし。音声メッセージはファイルアップロードのプレースホルダーとして表示されます。\",\n \"providerTypeWhisperApi\": \"Whisper API\",\n \"providerTypeWhisperApiDesc\": \"設定済みプロバイダー(OpenAI、Ollama など)の OpenAI 互換 Whisper API エンドポイントを使用します。\",\n \"providerTypeLocalWhisper\": \"ローカル Whisper\",\n \"providerTypeLocalWhisperDesc\": \"ローカルにインストールされた openai-whisper Python ライブラリで文字起こしを実行します。ffmpeg と openai-whisper の両方のインストールが必要です。\",\n \"localWhisperReady\": \"ローカル Whisper は準備完了です。ffmpeg と openai-whisper がインストールされています。\",\n \"localWhisperMissing\": \"ローカル Whisper は準備ができていません。不足している依存関係をインストールしてください。\",\n \"localWhisperMissingDesc\": \"ffmpeg: {{ffmpeg}} | openai-whisper: {{whisper}}。不足している依存関係をインストールしてください:ffmpeg(システムパッケージ)と openai-whisper(uv pip install openai-whisper、または QwenPaw を [whisper] エクストラ付きでインストール)。\",\n \"providerLabel\": \"Whisper API プロバイダー\",\n \"providerDescription\": \"Whisper API による音声文字起こしに使用するプロバイダーを選択します。Whisper 対応エンドポイントを持つプロバイダーのみ表示されます。\",\n \"providerPlaceholder\": \"プロバイダーを選択...\",\n \"noProvidersWarning\": \"文字起こし対応のプロバイダーが見つかりません。音声文字起こしを有効にするには、OpenAI プロバイダーを設定してください。\",\n \"transcriptionInfoTitle\": \"文字起こしの仕組み\",\n \"transcriptionInfoDesc\": \"Whisper API 文字起こしは OpenAI 互換の /v1/audio/transcriptions エンドポイントを使用します。Whisper 対応エンドポイントを持つプロバイダー(例: OpenAI)の設定が必要です。上記で具体的なプロバイダーを選択して文字起こしを有効にしてください。\",\n \"transcriptionInfoDescLocal\": \"ローカル Whisper 文字起こしは、お使いのマシン上で直接 openai-whisper ライブラリを実行します。ffmpeg(音声デコード用)と openai-whisper Python パッケージの両方のインストールが必要です。API キーやネットワーク接続は不要です。インストール:uv pip install 'qwenpaw[whisper]'。\"\n },\n \"acp\": {\n \"title\": \"ACP\",\n \"loading\": \"ACP 設定を読み込み中...\",\n \"builtin\": \"組み込み\",\n \"custom\": \"カスタム\",\n \"create\": \"カスタム Agent を追加\",\n \"createTitle\": \"ACP Agent を作成\",\n \"createSuccess\": \"ACP Agent を作成しました\",\n \"editTitle\": \"ACP 設定を編集\",\n \"agentKey\": \"Agent Key\",\n \"agentKeyRequired\": \"Agent Key を入力してください\",\n \"agentKeyInvalid\": \"Agent Key には英字、数字、アンダースコア、ハイフンのみ使用できます\",\n \"agentKeyExists\": \"この Agent Key は既に存在します\",\n \"enabled\": \"有効\",\n \"command\": \"コマンド\",\n \"commandRequired\": \"コマンドを入力してください\",\n \"args\": \"引数\",\n \"argsHelp\": \"1 行につき 1 つの引数\",\n \"env\": \"環境変数\",\n \"envHelp\": \"各行を KEY=VALUE 形式で入力してください\",\n \"envInvalidLine\": \"無効な環境変数形式: {{line}}\",\n \"trusted\": \"信頼済み\",\n \"toolParseMode\": \"ツール解析モード\",\n \"toolParseModeRequired\": \"ツール解析モードを選択してください\",\n \"stdioBufferLimit\": \"Stdio バッファ上限\",\n \"stdioBufferLimitHelp\": \"ACP サブプロセスの stdio 1 行読み取りバッファの最大バイト数です。\",\n \"stdioBufferLimitRequired\": \"stdio バッファ上限を入力してください\",\n \"stdioBufferLimitMin\": \"stdio バッファ上限は 1 バイト以上である必要があります\",\n \"docs\": \"設定ドキュメント\",\n \"docsHelp\": \"ACP 統合ドキュメントを開き、「How to configure external runners」セクションへ移動します\",\n \"notSet\": \"未設定\",\n \"configSaved\": \"ACP 設定を保存しました\",\n \"configFailed\": \"ACP 設定の保存に失敗しました\",\n \"deleteTitle\": \"{{name}} を削除\",\n \"deleteConfirm\": \"この ACP Agent を削除しますか?この操作は元に戻せません。\",\n \"deleteSuccess\": \"ACP Agent を削除しました\",\n \"deleteFailed\": \"ACP Agent の削除に失敗しました\",\n \"nodeSettings\": \"Node 設定\",\n \"nodePath\": \"Node パス\",\n \"chooseOtherNode\": \"別の Node を選択...\",\n \"selectNodePath\": \"Node パスを選択\",\n \"nodePathPrompt\": \"node 実行ファイルのパスを入力してください\",\n \"nodeSaved\": \"Node 設定を保存しました\",\n \"nodeLoadFailed\": \"Node 設定の読み込みに失敗しました\",\n \"nodeSaveFailed\": \"Node パスを利用できません\",\n \"nodeRuntime\": {\n \"bundled\": \"同梱 Node\",\n \"system\": \"システム Node\",\n \"custom\": \"カスタム Node\"\n },\n \"nodeRuntimeReason\": {\n \"systemNodeMissing\": \"システム Node が検出されませんでした\",\n \"nodeMissing\": \"Node パスが存在しません\",\n \"npxMissing\": \"npx が見つかりません\",\n \"versionCheckFailed\": \"バージョン確認に失敗しました\",\n \"unavailable\": \"利用不可\"\n }\n },\n \"backup\": {\n \"title\": \"バックアップ\",\n \"description\": \"エージェントのワークスペースと設定をバックアップ・復元します。\",\n \"create\": \"バックアップ作成\",\n \"import\": \"インポート\",\n \"export\": \"エクスポート\",\n \"restore\": \"復元\",\n \"delete\": \"削除\",\n \"batchDelete\": \"一括削除\",\n \"batchDeleteConfirm\": \"{{count}}件のバックアップを削除しますか?この操作は元に戻せません。\",\n \"deleteConfirm\": \"このバックアップを削除しますか?この操作は元に戻せません。\",\n \"name\": \"名前\",\n \"createdAt\": \"作成日時\",\n \"scopeSummary\": \"バックアップ範囲\",\n \"descriptionLabel\": \"説明\",\n \"noBackups\": \"バックアップがありません。現在の設定をバックアップするために作成してください。\",\n \"loadFailed\": \"バックアップ一覧の読み込みに失敗しました\",\n \"createTitle\": \"バックアップ作成\",\n \"backupMode\": \"バックアップモード\",\n \"fullBackup\": \"完全バックアップ\",\n \"fullBackupDesc\": \"すべてのエージェントワークスペース、グローバル設定、スキルプール、シークレット情報を含むすべてをバックアップ\",\n \"customBackup\": \"カスタムバックアップ\",\n \"partialBackup\": \"部分バックアップ\",\n \"partialBackupDesc\": \"バックアップする内容をカスタマイズ\",\n \"nameRequired\": \"バックアップ名は必須です\",\n \"namePlaceholder\": \"バックアップ名を入力\",\n \"descriptionPlaceholder\": \"説明(任意)...\",\n \"scopeAgents\": \"エージェントワークスペース\",\n \"scopeGlobalConfig\": \"グローバル設定\",\n \"scopeSkillPool\": \"スキルプール\",\n \"scopeSecrets\": \"シークレット情報\",\n \"scopeSecretsHint\": \"モデルプロバイダーシークレット(API キー)、環境変数などの機密情報を含みます\",\n \"securityNotice\": \"このバックアップには機密 credential 情報が含まれる可能性があります。エージェントワークスペースにはチャネル認証情報(bot トークン、アプリシークレットなど)が含まれ、モデルプロバイダーシークレットには API キーが含まれます。バックアップファイルを他人と共有しないでください。\",\n \"searchPlaceholder\": \"名前または ID でバックアップを検索...\",\n \"total\": \"合計 {{count}} 件\",\n \"agentsPlaceholder\": \"エージェントワークスペースを選択(空白の場合はすべて)\",\n \"selectAll\": \"すべて選択\",\n \"deselectAll\": \"選択解除\",\n \"createSuccess\": \"バックアップを作成しました\",\n \"createFailed\": \"バックアップの作成に失敗しました\",\n \"restoreTitle\": \"バックアップを復元\",\n \"restoreScope\": \"復元範囲\",\n \"restoreFullWarning\": \"全体復元は現在のインスタンスのすべての内容(すべてのエージェント、グローバル設定、スキルプール、シークレット)を完全に置き換えます。この操作は元に戻せません。復元中は関連機能が利用できません。復元完了後にサービスを再起動してください。\",\n \"agentGroupExisting\": \"既存\",\n \"agentGroupNew\": \"新規\",\n \"agentExists\": \"既存\",\n \"agentNew\": \"新規\",\n \"agentActionReplace\": \"置き換え\",\n \"agentActionAdd\": \"追加\",\n \"agentColumnName\": \"エージェント\",\n \"agentColumnWorkspace\": \"ワークスペース\",\n \"agentTotal\": \"合計 {{count}} 件\",\n \"noAgentsInBackup\": \"バックアップにエージェントワークスペースがありません\",\n \"loadingAgents\": \"エージェントリストを読み込み中...\",\n \"agentSearchPlaceholder\": \"名前またはIDで検索...\",\n \"agentSearchTotal\": \"{{total}} 件中 {{count}} 件\",\n \"restoreCustomSummary\": \"既存 {{existing}} 件 · 新規 {{added}} 件を選択\",\n \"defaultWorkspaceDirUnused\": \"新規エージェントなし\",\n \"otherOptions\": \"その他のオプション\",\n \"defaultWorkspaceDirDefault\": \"<デフォルトパス>/{{aid}}\",\n \"restoreWarningModify\": \"復元操作により現在の設定が変更されます。この操作は元に戻せません。\",\n \"restoreWarningRestart\": \"復元中は関連機能が利用できません。復元完了後にサービスを再起動してください。\",\n \"preRestoreBackupTitle\": \"復元前バックアップの作成\",\n \"preRestoreBackupContent\": \"復元操作は不可逆です。まず現在の状態のバックアップを作成しますか?復元中に問題が発生した場合、現在の状態にロールバックできます。\",\n \"preRestoreBackupYes\": \"はい、先にバックアップを作成\",\n \"preRestoreBackupNo\": \"いいえ、直接復元\",\n \"preRestoreBackupDesc\": \"復元前に自動作成されたバックアップ\",\n \"preRestoreBackupFailed\": \"復元前バックアップの作成に失敗しました\",\n \"preRestoreBackupSuccess\": \"復元前バックアップが作成されました。ID:{{id}}\",\n \"creatingPreRestoreBackup\": \"復元前バックアップを作成中...\",\n \"skipAndRestore\": \"スキップして復元を続行\",\n \"restoreConfirm\": \"このバックアップを復元することを確認します\",\n \"restoreMode\": \"復元モード\",\n \"restoreModeFull\": \"全体復元\",\n \"restoreModeFullDesc\": \"現在のインスタンスのすべての内容を完全に置き換えます。すべてのエージェント、グローバル設定、スキルプール、シークレットが含まれます\",\n \"restoreModeCustom\": \"カスタム復元\",\n \"restoreModeCustomDesc\": \"復元する内容を選択します(特定のエージェント、設定など);今回の復元範囲外の既存エージェントは削除されません\",\n \"restoreModeFullDisabled\": \"フルバックアップのみ\",\n \"defaultWorkspaceDir\": \"エージェントデフォルトワークスペースディレクトリ\",\n \"defaultWorkspaceDirPlaceholder\": \"空欄の場合は ~/.qwenpaw/workspaces を使用\",\n \"defaultWorkspaceDirHint\": \"エージェントの元のワークスペースパスが存在しない場合、エージェントは <デフォルトパス>/<エージェントID> に保存されます。存在する場合は元のパスが使用されます。\",\n \"restoreStrategy\": \"復元戦略\",\n \"restoreStrategyPreserve\": \"ローカルのセキュリティ設定と MCP を保持\",\n \"restoreStrategyPreserveDesc\": \"このインスタンスのセキュリティ保護と MCP 設定を保持します。\",\n \"restoreStrategyRestore\": \"これらの設定をバックアップから復元\",\n \"restoreStrategyRestoreDesc\": \"バックアップ内のセキュリティ設定と MCP 設定を使用します。\",\n \"restoreSuccess\": \"バックアップを復元しました。サービスを再起動してください。\",\n \"restoreSuccessPreserved\": \"バックアップを復元しました。ローカル設定を保持しました: {{keys}}。サービスを再起動してください。\",\n \"restoreSuccessWithBackup\": \"バックアップを復元しました。復元前バックアップID:{{preRestoreId}}。サービスを再起動してください。\",\n \"restoreFailed\": \"バックアップの復元に失敗しました\",\n \"restoreTargetBusy\": \"これらのディレクトリが使用中のため、復元に失敗しました。使用しているブラウザまたはプロセスを閉じてから再試行してください。それでもロックが解除されない場合は、システムを再起動してからもう一度お試しください。\",\n \"detailLoadFailed\": \"バックアップ詳細の読み込みに失敗しました。復元はできません\",\n \"trustLocalBanner\": \"ローカルバックアップ - 既定では完全復元\",\n \"trustForeignBanner\": \"インポートされたバックアップ - 既定ではローカルのセキュリティ設定と MCP を保持します\",\n \"trustLegacyBanner\": \"レガシーバックアップ - 復元前に信頼の確認が必要です\",\n \"trustForeignTitle\": \"このバックアップを信頼しますか?\",\n \"trustLegacyTitle\": \"レガシーバックアップを信頼しますか?\",\n \"unknownBackupName\": \"バックアップアーカイブ\",\n \"trustForeignDesc\": \"このバックアップはこのインスタンスで署名されていません。提供元を信頼できる場合のみ続行してください。復元時には既定でローカルのセキュリティ設定と MCP 設定が保持されます。\",\n \"trustLegacyDesc\": \"この古いバックアップにはローカル署名がありません。提供元を信頼できる場合のみ続行してください。復元前にこのインスタンスで署名します。\",\n \"deleteSuccess\": \"バックアップを削除しました\",\n \"deleteFailed\": \"バックアップの削除に失敗しました\",\n \"exportFailed\": \"バックアップのエクスポートに失敗しました\",\n \"exportWarningTitle\": \"機密情報の警告\",\n \"exportWarningContent\": \"このバックアップには機密の認証情報が含まれている可能性があります。エージェントワークスペースにはチャネル認証情報(ボットトークン、アプリシークレットなど)が含まれ、モデルプロバイダーのシークレットにはAPIキーが含まれます。バックアップファイルを他人と共有しないでください。\",\n \"exportConfirm\": \"エクスポートを確認\",\n \"importTitle\": \"バックアップをインポート\",\n \"importSuccess\": \"バックアップをインポートしました\",\n \"importFailed\": \"バックアップのインポートに失敗しました\",\n \"importConflictTitle\": \"バックアップが既に存在します\",\n \"importConflictDesc\": \"同じIDのバックアップが既に存在しています。既存のバックアップを上書きしますか?\",\n \"importReplace\": \"上書き\",\n \"agents\": \"{{count}}個のエージェント\",\n \"allAgents\": \"すべてのエージェント\",\n \"globalConfig\": \"グローバル設定\",\n \"skillPool\": \"スキルプール\",\n \"secrets\": \"シークレット\",\n \"selected\": \"{{count}}件選択中\",\n \"fileCount\": \"{{count}}ファイル\",\n \"totalSize\": \"{{size}}\",\n \"progressStarting\": \"バックアップを初期化中...\",\n \"progressAgent\": \"エージェント {{index}} / {{total}} をバックアップ中...\",\n \"progressSaving\": \"バックアップファイルを保存中...\",\n \"progressDone\": \"バックアップ完了\",\n \"localModelsNotice\": \"バックアップファイルにはローカルモデルファイルは含まれません。デバイス間の移行の場合は、対象デバイスで必要なローカルモデルを再ダウンロードしてください。\"\n },\n \"approval\": {\n \"title\": \"セキュリティ承認が必要です\",\n \"tool\": \"ツール\",\n \"severity\": \"重要度\",\n \"findings\": \"検出件数\",\n \"parameters\": \"パラメータ\",\n \"approve\": \"承認\",\n \"deny\": \"拒否\",\n \"source\": \"ソース\",\n \"subSession\": \"サブエージェント\",\n \"approved\": \"ツール呼び出しが承認されました\",\n \"denied\": \"ツール呼び出しが拒否されました\",\n \"approveFailed\": \"承認に失敗しました\",\n \"denyFailed\": \"拒否に失敗しました\",\n \"approvePattern\": \"常に許可\",\n \"strictModeHint\": \"厳格モードでは常に許可できません\",\n \"approveExact\": \"今回のみ\",\n \"approvalScope\": \"承認スコープ\",\n \"details\": \"詳細\",\n \"timeoutAutoDenied\": \"タイムアウト、自動拒否\",\n \"acknowledge\": \"了解\",\n \"builtinSource\": \"組み込み\",\n \"ownerAgent\": \"オーナーエージェント\",\n \"executingAgent\": \"実行エージェント\"\n },\n \"pluginManager\": {\n \"title\": \"プラグインマネージャー\",\n \"description\": \"サービスを再起動せずにプラグインのインストール、管理、削除を行います。\",\n \"installed\": \"インストール済みプラグイン\",\n \"noPlugins\": \"プラグインがまだインストールされていません\",\n \"installBtn\": \"プラグインをインストール\",\n \"installTitle\": \"プラグインのインストール\",\n \"dropPrimary\": \"フォルダまたはZIPファイルをドロップ\",\n \"dropSecondary\": \"またはクリックしてZIPファイルを選択\",\n \"orFromUrl\": \"またはURLから\",\n \"installFromUrl\": \"URLからインストール\",\n \"urlPlaceholder\": \"https://example.com/plugin.zip\",\n \"zipOnly\": \"ファイルとしてドロップできるのはZIPファイルのみです — ディレクトリからインストールする場合はフォルダをドラッグしてください\",\n \"dropFailed\": \"ドロップされたフォルダの読み取りに失敗しました\",\n \"restartHint\": \"一部のプラグイン(例:フック、monkey-patch)は、完全に有効化または削除するためにアプリの再起動が必要な場合があります。\",\n \"installing\": \"インストール中...\",\n \"installSuccess\": \"プラグインのインストールに成功しました\",\n \"installFailed\": \"プラグインのインストールに失敗しました\",\n \"uninstall\": \"アンインストール\",\n \"uninstallConfirm\": \"プラグイン \\\"{{name}}\\\" をアンインストールしますか?この操作は取り消せません。一部のプラグインは、完全に削除するためにアプリの再起動が必要な場合があります。\",\n \"uninstalling\": \"アンインストール中...\",\n \"uninstallSuccess\": \"プラグインのアンインストールに成功しました\",\n \"uninstallFailed\": \"プラグインのアンインストールに失敗しました\",\n \"statusLoaded\": \"実行中\",\n \"statusUnloaded\": \"未ロード\",\n \"type\": \"タイプ\",\n \"version\": \"バージョン\",\n \"author\": \"作成者\",\n \"unknown\": \"不明\",\n \"loadFailed\": \"プラグイン一覧の読み込みに失敗しました\",\n \"confirmTitle\": \"アンインストールの確認\",\n \"officialTitle\": \"公式プラグイン\",\n \"officialDesc\": \"ダウンロード CDN から公開済みプラグインをインストールします。\",\n \"catalogRefresh\": \"更新\",\n \"catalogEmpty\": \"利用可能な公式プラグインがありません(CDN カタログが空の可能性があります)\",\n \"catalogLoadFailed\": \"公式プラグインカタログの読み込みに失敗しました\",\n \"catalogInstall\": \"インストール\",\n \"catalogReinstall\": \"再インストール\",\n \"catalogUpgrade\": \"アップグレード可能\",\n \"catalogUpgradeBtn\": \"アップグレード\",\n \"catalogInstalled\": \"インストール済み\",\n \"catalogSha256\": \"SHA256\",\n \"kindBundle\": \"バンドル\",\n \"kindTool\": \"ツール\",\n \"filterByName\": \"名前で絞り込み\",\n \"filterByKind\": \"タグで絞り込み\",\n \"marketTitle\": \"Plugin Market\",\n \"marketEmpty\": \"No plugins found in market\",\n \"marketLoadFailed\": \"Failed to load plugin market\",\n \"marketUnavailable\": \"Plugin Market is currently under maintenance, please try again later\",\n \"marketAll\": \"All\",\n \"marketSearch\": \"Search plugins...\",\n \"marketCategory\": \"Filter by category\",\n \"marketSearchResult\": \"Found {{count}} plugins matching \\\"{{keyword}}\\\"\",\n \"marketDetails\": \"Details\",\n \"marketDownloads\": \"Downloads\",\n \"marketDeveloper\": \"Developer\",\n \"marketSortLabel\": \"Sort plugins\",\n \"marketSortDownloads\": \"Most downloaded\",\n \"marketSortUpdated\": \"Recently updated\",\n \"marketSortFavorites\": \"Most favorited\"\n },\n \"inbox\": {\n \"title\": \"受信トレイ\",\n \"tabApprovals\": \"承認\",\n \"tabPushMessages\": \"プッシュ通知\",\n \"tabHarvests\": \"AI Harvest\",\n \"summaryApprovals\": \"承認件数\",\n \"summaryPushUnread\": \"未読通知\",\n \"summaryHarvests\": \"Harvest\",\n \"from\": \"送信元:\",\n \"requestedBy\": \"リクエスト元\",\n \"markRead\": \"既読にする\",\n \"markAllRead\": \"すべて既読\",\n \"markAllReadNoUnread\": \"未読メッセージはありません\",\n \"markAllReadSuccess\": \"{{count}} 件を既読にしました\",\n \"selectAllCurrentPage\": \"このページを全選択\",\n \"selectedItems\": \"{{count}} 件選択中\",\n \"batchDeleteButton\": \"一括削除\",\n \"batchOperation\": \"一括操作\",\n \"exitBatch\": \"一括終了\",\n \"batchDeleteConfirm\": \"選択した {{count}} 件の通知を削除しますか?\",\n \"batchDeleteSuccess\": \"{{count}} 件の通知を削除しました\",\n \"pushCronHeader\": \"定期実行:{{name}}\",\n \"viewDetails\": \"詳細を見る\",\n \"deleteMessageConfirm\": \"この通知を削除しますか?\",\n \"viewAll\": \"すべて表示\",\n \"read\": \"既読\",\n \"approve\": \"承認\",\n \"reject\": \"拒否\",\n \"ready\": \"準備完了\",\n \"harvestNow\": \"今すぐ実行\",\n \"createHarvest\": \"Harvest を作成\",\n \"createFirstHarvest\": \"最初の Harvest を作成\",\n \"createSuccess\": \"Harvest を作成しました\",\n \"emptyApprovals\": \"保留中の承認はありません\",\n \"emptyPush\": \"通知はありません\",\n \"wobbleEnable\": \"承認の揺れ通知を有効にする\",\n \"wobbleDisable\": \"承認の揺れ通知を無効にする\",\n \"emptyHarvests\": \"Harvest はまだありません\",\n \"statusReadyToHarvest\": \"実行可能\",\n \"statusGrowing\": \"実行中\",\n \"harvestedTimes\": \"{{count}} 回実行済み\",\n \"harvestSuccessRate\": \"成功率 {{rate}}%\",\n \"messageDetailComingSoon\": \"詳細表示は準備中です\",\n \"harvestSettingsComingSoon\": \"Harvest 設定は準備中です\",\n \"messageDetailTitle\": \"実行詳細\",\n \"detailCronTitle\": \"定期実行:{{name}}\",\n \"detailHeartbeatTitle\": \"ハートビート\",\n \"messageNotFound\": \"メッセージが見つかりません\",\n \"detailTaskName\": \"タスク名\",\n \"detailTaskId\": \"タスクID\",\n \"detailStatus\": \"実行ステータス\",\n \"detailSeverity\": \"重要度\",\n \"detailAgent\": \"所属Agent\",\n \"detailSource\": \"ソース\",\n \"detailSourceCronJob\": \"定期実行\",\n \"detailSourceHeartbeat\": \"ハートビート\",\n \"detailTrigger\": \"トリガー\",\n \"detailDuration\": \"所要時間\",\n \"detailExecutedAt\": \"実行時刻\",\n \"detailReceivedAt\": \"受信時刻\",\n \"detailRunId\": \"実行ID\",\n \"detailContent\": \"出力\",\n \"detailPayload\": \"追加情報\",\n \"detailExecutionTrace\": \"実行トレース\",\n \"detailTraceEmpty\": \"実行トレースはありません\",\n \"filterByAgent\": \"エージェントで絞り込み\",\n \"filterBySourceType\": \"ソースで絞り込み\",\n \"sourceTypeCron\": \"Cron\",\n \"sourceTypeHeartbeat\": \"Heartbeat\",\n \"sourceTypeMemory\": \"Memory\"\n },\n \"codingMode\": {\n \"title\": \"コーディングモード\",\n \"experimental\": \"実験的機能\",\n \"experimentalDesc\": \"コーディングモードは QwenPaw を軽量な Web IDE に変換し、コードエディター、ファイルツリー、Git 統合、AI コーディング支援を提供します。\",\n \"experimentalNote\": \"この機能は実験的であり、現在も積極的に開発中です。一部の機能は制限されているか、将来のアップデートで変更される場合があります。\",\n \"confirmBtn\": \"コーディングモードを有効にする\",\n \"enterTooltip\": \"コーディングモードに入る\",\n \"exitTooltip\": \"コーディングモードを終了する\",\n \"btnCode\": \"コード\",\n \"btnChat\": \"チャット\",\n \"selectProject\": \"コーディングプロジェクトを選択\",\n \"selectProjectDesc\": \"作業するプロジェクトを選択するか、デフォルトのワークスペースを使用してください\",\n \"tabWorkspace\": \"デフォルトワークスペース\",\n \"tabClone\": \"リポジトリをクローン\",\n \"tabLocal\": \"ローカルプロジェクトをインポート\",\n \"tabNew\": \"新規プロジェクト\",\n \"workspaceDesc\": \"現在のエージェントのワークスペースディレクトリを使用\",\n \"cloneUrl\": \"GitリポジトリURL\",\n \"cloneUrlPlaceholder\": \"https://github.com/user/repo.git\",\n \"cloneName\": \"プロジェクト名(オプション)\",\n \"cloneNamePlaceholder\": \"空白の場合はURLから推測\",\n \"cloneBtn\": \"クローン開始\",\n \"cloning\": \"クローン中...\",\n \"localPath\": \"ローカルディレクトリパス\",\n \"localPathPlaceholder\": \"/Users/yourname/my-project\",\n \"localPathHint\": \"ローカルプロジェクトへの絶対パスを入力してください\",\n \"openBtn\": \"インポート\",\n \"newName\": \"プロジェクト名\",\n \"newNamePlaceholder\": \"my-project\",\n \"createBtn\": \"プロジェクト作成\",\n \"switchProject\": \"プロジェクト切替\",\n \"currentProject\": \"現在のプロジェクト\",\n \"resetToWorkspace\": \"デフォルトワークスペースにリセット\",\n \"dropPrimary\": \"フォルダをここにドラッグ、またはクリックして参照\",\n \"dropSecondary\": \"Finderからドラッグするか、下にパスを貼り付け\",\n \"pathHint\": \"絶対パスを入力してください(/ または ~ で始まる)\",\n \"importing\": \"インポート中...\",\n \"dropFailed\": \"フォルダの読み取りに失敗\",\n \"workingDir\": \"作業ディレクトリ\",\n \"importCopyNote\": \"フォルダは coding_projects/ にコピーされます。元のファイルは変更されません。\",\n \"importCopyDesc\": \"インポートするとフォルダのコピーが作成され、Agent はコピー上で作業します。元のファイルは変更されません。元のディレクトリで直接作業するには「ディレクトリを開く」を使用してください。\",\n \"tabOpenDir\": \"ディレクトリを開く\",\n \"openDirDesc\": \"ローカルの既存ディレクトリを直接開きます(ファイルはコピーされません)\",\n \"openDirBtn\": \"このディレクトリを開く\",\n \"openDirLoading\": \"読み込み中...\",\n \"openDirEmpty\": \"サブディレクトリがありません\",\n \"openDirHome\": \"ホーム\",\n \"openDirRefresh\": \"更新\",\n \"openDirHiddenFolders\": \"隠しフォルダ\"\n },\n \"market\": {\n \"searchPlaceholder\": \"プラットフォームを横断してスキルを検索\",\n \"categoryAll\": \"すべて\",\n \"categoryPlaceholder\": \"カテゴリを検索\",\n \"searchResult\": \"「{{keyword}}」に一致するスキルが {{count}} 件見つかりました\",\n \"providerUnavailable\": \"ソースが利用できません\",\n \"noResults\": \"結果が見つかりません\",\n \"startTyping\": \"上の検索ボックスにキーワードを入力してスキルを探します\",\n \"browseHint\": \"{{providers}} のスキルを閲覧するには、カテゴリを選択するかキーワードを入力してください\",\n \"noDescription\": \"説明はありません\",\n \"browseMarket\": \"マーケットを閲覧\",\n \"installQueue\": \"インストールキュー\",\n \"clearCompleted\": \"クリア\",\n \"retry\": \"再試行\",\n \"status\": {\n \"queued\": \"待機中\",\n \"installing\": \"インストール中\",\n \"completed\": \"完了\",\n \"failed\": \"失敗\",\n \"cancelled\": \"キャンセル済み\"\n },\n \"loadMore\": \"もっと読み込む\",\n \"noMoreResults\": \"これ以上ありません\",\n \"detail\": {\n \"title\": \"スキルの詳細\",\n \"version\": \"バージョン\",\n \"author\": \"作成者\",\n \"sourceUrl\": \"インストール URL\",\n \"slug\": \"識別子\",\n \"missing\": \"—\"\n },\n \"stats\": {\n \"downloads\": \"ダウンロード数\",\n \"stars\": \"スター\",\n \"installs\": \"インストール数\",\n \"likes\": \"いいね数\",\n \"views\": \"閲覧数\",\n \"category\": \"カテゴリ\",\n \"updatedAt\": \"更新日\"\n },\n \"queueMsg\": {\n \"timedOut\": \"インストールがタイムアウトしました\",\n \"failedPrefix\": \"失敗:{{msg}}\"\n }\n },\n \"startup\": {\n \"starting\": \"バックエンドを起動中...\",\n \"checking\": \"バックエンドに接続中...\",\n \"error\": \"バックエンドの起動に失敗しました。\",\n \"errorHint\": \"バックエンドプロセスを起動できませんでした。詳細はアプリケーションログを確認してください。\",\n \"errorDetails\": \"エラーの詳細を表示\",\n \"timeout\": \"{{seconds}} 秒以内にバックエンドの起動に失敗しました。\",\n \"timeoutHint\": \"バックエンドの起動に失敗しました。再試行するか、アプリケーションのログを確認してください。\",\n \"retry\": \"再試行\"\n },\n \"preview\": {\n \"error\": {\n \"SENSITIVE_FILE_BLOCKED\": \"このファイルはファイルガードによりブロックされています(機密ファイル保護)。\",\n \"OUTSIDE_WORKSPACE\": \"このファイルはワークスペースの範囲外です。設定 → セキュリティ → ファイルガード で「コンソールでワークスペース外ファイルのプレビューを許可」を有効にしてください。\",\n \"FORBIDDEN\": \"アクセス拒否:このファイルをプレビューする権限がありません。\",\n \"NOT_FOUND\": \"ファイルが見つかりません。移動または削除された可能性があります。相対パスを使用している場合は、絶対パスをお試しください。\",\n \"LOAD_FAILED\": \"ファイルプレビューの読み込みに失敗しました。\",\n \"LOAD_FAILED_DETAIL\": \"ファイルプレビューの読み込みに失敗しました: {{detail}}\"\n }\n },\n \"tool\": {\n \"readFile\": \"{{file}} を読み込む\",\n \"readFileDefault\": \"ファイルを読み込む\",\n \"writeFile\": \"{{file}} を作成\",\n \"writeFileDefault\": \"ファイルを作成\",\n \"editFile\": \"{{file}} を編集\",\n \"editFileDefault\": \"ファイルを編集\",\n \"appendFile\": \"{{file}} に追記\",\n \"appendFileDefault\": \"ファイルに追記\",\n \"grepSearch\": \"Grep {{pattern}}\",\n \"grepSearchDefault\": \"Grep 検索\",\n \"globSearch\": \"Glob {{pattern}}\",\n \"globSearchDefault\": \"Glob 検索\",\n \"viewImage\": \"画像 {{file}} を表示\",\n \"viewImageDefault\": \"画像を表示\",\n \"viewVideo\": \"動画 {{file}} を表示\",\n \"viewVideoDefault\": \"動画を表示\",\n \"desktopScreenshot\": \"デスクトップのスクリーンショット\",\n \"sendFile\": \"{{file}} を送信\",\n \"sendFileDefault\": \"ファイルを送信\",\n \"browserUse\": \"{{detail}} をブラウズ\",\n \"browserNavigate\": \"{{url}} を開く\",\n \"browserNavigateDefault\": \"ページを開く\",\n \"browserClick\": \"クリック\",\n \"browserType\": \"テキストを入力\",\n \"browserSnapshot\": \"スナップショットを取得\",\n \"browserScroll\": \"スクロール\",\n \"getCurrentTime\": \"現在時刻を取得\",\n \"setTimezone\": \"{{timezone}} に設定\",\n \"getTokenUsage\": \"トークン使用量を照会\",\n \"memorySearch\": \"メモリで {{query}} を検索\",\n \"memorySearchDefault\": \"メモリを検索\",\n \"listAgents\": \"エージェント一覧\",\n \"chatWithAgent\": \"{{agent}} とチャット\",\n \"chatWithAgentDefault\": \"エージェントとチャット\",\n \"submitToAgent\": \"{{agent}}{{task}} に送信\",\n \"submitToAgentDefault\": \"タスクを送信\",\n \"checkAgentTask\": \"{{agent}} #{{taskId}} を確認\",\n \"checkAgentTaskAgent\": \"{{agent}} を確認\",\n \"checkAgentTaskDefault\": \"タスクを確認\",\n \"delegateExternalAgent\": \"{{runner}} に委任\",\n \"delegateExternalAgentDefault\": \"外部エージェントに委任\",\n \"materializeSkill\": \"{{skill}} を作成\",\n \"materializeSkillDefault\": \"スキルを作成\",\n \"shell\": \"{{command}} を実行\",\n \"shellDefault\": \"コマンドを実行\",\n \"formatTable\": {\n \"file\": \"ファイル\",\n \"lineNumber\": \"行番号\",\n \"score\": \"スコア\",\n \"summary\": \"概要\",\n \"name\": \"名前\",\n \"id\": \"ID\",\n \"description\": \"説明\",\n \"status\": \"ステータス\"\n },\n \"loading\": \"読み込み中...\",\n \"inputProgress\": \"{{count}} 文字を受信\",\n \"rawInputPreview\": \"リアルタイムの未加工入力(末尾 4,096 文字)\",\n \"browserAction\": {\n \"startHeaded\": \"ブラウザを起動(表示あり)\",\n \"start\": \"ブラウザを起動\",\n \"stop\": \"ブラウザを停止\",\n \"open\": \"{{url}} を開く\",\n \"openDefault\": \"ページを開く\",\n \"navigate\": \"{{url}} に移動\",\n \"navigateDefault\": \"ページに移動\",\n \"navigateBack\": \"戻る\",\n \"click\": \"{{selector}} をクリック\",\n \"clickDefault\": \"要素をクリック\",\n \"type\": \"{{text}} を入力\",\n \"typeDefault\": \"テキストを入力\",\n \"snapshot\": \"スナップショットを取得\",\n \"screenshot\": \"スクリーンショット {{path}}\",\n \"screenshotDefault\": \"スクリーンショット\",\n \"eval\": \"{{code}} を評価\",\n \"evalDefault\": \"コードを評価\",\n \"runCode\": \"{{code}} を実行\",\n \"runCodeDefault\": \"コードを実行\",\n \"closePage\": \"ページを閉じる\",\n \"tabs\": \"タブ {{action}}\",\n \"tabsDefault\": \"タブを管理\",\n \"fillForm\": \"フォームを入力\",\n \"fileUpload\": \"{{filename}} をアップロード\",\n \"fileUploadDefault\": \"ファイルをアップロード\",\n \"fileDownload\": \"{{target}} をダウンロード\",\n \"fileDownloadDefault\": \"ファイルをダウンロード\",\n \"pressKey\": \"{{key}} を押す\",\n \"pressKeyDefault\": \"キーを押す\",\n \"hover\": \"{{selector}} にホバー\",\n \"hoverDefault\": \"要素にホバー\",\n \"drag\": \"ドラッグ\",\n \"selectOption\": \"オプションを選択\",\n \"waitFor\": \"{{target}} を待機\",\n \"waitForDefault\": \"要素を待機\",\n \"resize\": \"{{w}}x{{h}} にリサイズ\",\n \"resizeDefault\": \"ウィンドウをリサイズ\",\n \"pdf\": \"PDF {{path}} に出力\",\n \"pdfDefault\": \"PDF に出力\",\n \"install\": \"インストール\",\n \"batch\": \"バッチ\"\n },\n \"lineBadge\": {\n \"lines\": \"{{count}} 行\",\n \"addLines\": \"+{{count}} 行\",\n \"delLines\": \"-{{count}} 行\",\n \"matches\": \"{{count}} 件\",\n \"files\": \"{{count}} ファイル\"\n },\n \"inlineResult\": {\n \"taskId\": \"タスク #{{id}}\",\n \"submitted\": \"送信済み\"\n },\n \"execute\": \"{{tool}} を実行\",\n \"executeDefault\": \"ツールを実行\"\n },\n \"loop\": {\n \"gotoSettings\": \"エージェント Loop 設定\",\n \"selectorTitle\": \"Loop モード\",\n \"selectorHint\": \"エージェントの進め方を選択\",\n \"selectorAria\": \"Loop モードを選択\",\n \"builtInModes\": \"組み込み\",\n \"customModes\": \"カスタムとプラグイン\",\n \"starting\": \"開始中\",\n \"running\": \"実行中\",\n \"awaiting_user\": \"返信待ち\",\n \"activePersistentDescription\": \"このモードは完了するか会話をリセットするまで有効です。\",\n \"activeCustomDescription\": \"このカスタムモードは /mode off、/clear、/new のいずれかを使用するまで有効です。\",\n \"loadError\": \"一部の Loop モードを読み込めませんでした。\",\n \"retry\": \"再試行\",\n \"modes\": {\n \"default\": {\n \"name\": \"デフォルト\",\n \"description\": \"標準の制御されたエージェント Loop。\"\n },\n \"goal\": {\n \"name\": \"目標\",\n \"description\": \"具体的で検証可能な目標に向かって継続的に取り組みます。\"\n },\n \"mission\": {\n \"name\": \"ミッション\",\n \"description\": \"構造化された複数ステップのタスクを継続的に実行します。\"\n }\n }\n }\n}\n"}}, "console/src/locales/ru.json": {"e524dfe78d4a69b2441d8f611935bbe0b28cb7da": {"t": 1785674213.6847112, "n": 186933, "h": "9a4a1ebeaff27366", "c": "{\n \"common\": {\n \"save\": \"Сохранить\",\n \"reset\": \"Сбросить\",\n \"cancel\": \"Отмена\",\n \"confirm\": \"Подтвердить\",\n \"delete\": \"Удалить\",\n \"edit\": \"Редактировать\",\n \"create\": \"Создать\",\n \"upload\": \"Загрузить\",\n \"download\": \"Скачать\",\n \"refresh\": \"Обновить\",\n \"search\": \"Поиск\",\n \"enable\": \"Включить\",\n \"disable\": \"Отключить\",\n \"enabled\": \"Включено\",\n \"disabled\": \"Отключено\",\n \"preview\": \"Предпросмотр\",\n \"view\": \"Просмотр\",\n \"content\": \"Содержимое\",\n \"loading\": \"Загрузка...\",\n \"saving\": \"Сохранение...\",\n \"copy\": \"Копировать\",\n \"copied\": \"Скопировано в буфер обмена\",\n \"copyFailed\": \"Не удалось скопировать в буфер обмена\",\n \"contentPlaceholder\": \"Введите содержимое...\",\n \"help\": \"Справка\",\n \"close\": \"Закрыть\",\n \"actions\": \"Действия\",\n \"total\": \"Всего {{count}}\",\n \"clear\": \"Очистить\",\n \"paste\": \"Вставить\"\n },\n \"harnesses\": {\n \"connected\": \"Подключено\",\n \"disconnected\": \"Отключено\",\n \"notConnected\": \"Не подключено\",\n \"comingSoon\": \"Скоро\",\n \"connect\": \"Подключить через ChatGPT\",\n \"disconnect\": \"Отключить\",\n \"apiKeyAuthenticated\": \"Аутентификация по API-ключу\",\n \"chatGptAuthenticated\": \"Подключено к ChatGPT\",\n \"cliAuthenticated\": \"{{type}} CLI аутентифицирован\"\n },\n \"desktop\": {\n \"closeWindow\": {\n \"title\": \"Закрыть окно\",\n \"description\": \"Что сделать при закрытии окна? Выход из приложения остановит все выполняющиеся задачи и запланированные задания.\",\n \"remember\": \"Запомнить мой выбор\",\n \"showWindow\": \"Показать окно\",\n \"minimizeToTray\": \"Свернуть в трей\",\n \"quitApp\": \"Выйти из приложения\",\n \"preference\": \"Закрытие окна\",\n \"askEveryTime\": \"Спрашивать каждый раз\"\n }\n },\n \"chunkError\": {\n \"title\": \"Не удалось загрузить страницу\",\n \"subTitle\": \"Возможно, это вызвано проблемой сети или обновлением приложения.\",\n \"genericTitle\": \"Что-то пошло не так\",\n \"genericSubTitle\": \"При отображении этой страницы произошла непредвиденная ошибка.\",\n \"reload\": \"Перезагрузить\"\n },\n \"nav\": {\n \"chat\": \"Чат\",\n \"control\": \"Управление\",\n \"channels\": \"Каналы\",\n \"sessions\": \"Сессии\",\n \"cronJobs\": \"Cron-задачи\",\n \"heartbeat\": \"Пульс\",\n \"agent\": \"Рабочая область\",\n \"workspace\": \"Файлы\",\n \"skills\": \"Навыки\",\n \"skillPool\": \"Пул навыков\",\n \"tools\": \"Инструменты\",\n \"mcp\": \"MCP\",\n \"agentConfig\": \"Конфигурация\",\n \"settings\": \"Настройки\",\n \"models\": \"Модели\",\n \"environments\": \"Окружения\",\n \"security\": \"Безопасность\",\n \"tokenUsage\": \"Использование токенов\",\n \"agentStats\": \"Статистика агента\",\n \"voiceTranscription\": \"Транскрипция голоса\",\n \"agents\": \"Управление агентами\",\n \"debug\": \"Отладка\",\n \"plugins\": \"Плагины\",\n \"acp\": \"ACP\",\n \"backups\": \"Резервные копии\",\n \"pluginManager\": \"Менеджер плагинов\"\n },\n \"debug\": {\n \"title\": \"Отладка\",\n \"desc\": \"Просмотр файла логов backend-демона для диагностики. Пока открыта эта страница, логи обновляются автоматически.\",\n \"level\": {\n \"all\": \"Все\"\n },\n \"backend\": {\n \"title\": \"Backend-логи\",\n \"autoRefresh\": \"Автообновление\",\n \"newestFirst\": \"Сначала новые\",\n \"updatedAt\": \"Обновлено\",\n \"path\": \"Файл лога\",\n \"notFound\": \"Файл backend-логов пока не найден.\",\n \"placeholder\": \"Здесь появится вывод backend-логов.\",\n \"loadFailed\": \"Не удалось загрузить backend-логи\",\n \"searchPlaceholder\": \"Поиск по backend-логам...\"\n },\n \"actions\": {\n \"refreshBackend\": \"Обновить backend-логи\",\n \"refreshSuccess\": \"Логи обновлены\",\n \"copyBackend\": \"Копировать backend-логи\"\n }\n },\n \"agent\": {\n \"parent\": \"Настройки\",\n \"agents\": \"Агенты\",\n \"management\": \"Управление агентами\",\n \"pageDescription\": \"Создавайте, настраивайте и управляйте несколькими AI-агентами с пользовательскими рабочими пространствами и идентичностями.\",\n \"name\": \"Название\",\n \"id\": \"ID\",\n \"description\": \"Описание\",\n \"workspace\": \"Путь к рабочему пространству\",\n \"backend\": {\n \"column\": \"Бэкенд\",\n \"eyebrow\": \"Архитектура агента\",\n \"typeTitle\": \"Выберите, как работает этот агент\",\n \"typeDescription\": \"Тип агента определяет работу всех диалогов в Chat, Coding и Channel.\",\n \"nativeTitle\": \"Нативный агент QwenPaw\",\n \"nativeBadge\": \"Нативный\",\n \"nativeDescription\": \"Использует настроенные LLM, Skills, инструменты, память и среду выполнения QwenPaw.\",\n \"thirdPartyTitle\": \"Сторонний агент\",\n \"thirdPartyBadge\": \"Сторонний\",\n \"thirdPartyDescription\": \"Использует авторизованную внешнюю среду агента вместо модели QwenPaw.\",\n \"providerTitle\": \"Выберите стороннего агента\",\n \"providerDescription\": \"Авторизация общая для этого устройства; проекты и диалоги изолированы для каждого агента.\",\n \"codexHint\": \"ChatGPT OAuth · Рабочая область агента\",\n \"qoderHint\": \"Учётная запись Qoder или PAT · Рабочая область агента\",\n \"account\": \"Учётная запись Codex\",\n \"qoderAccount\": \"Учётная запись Qoder\",\n \"binary\": \"Исполняемый файл Codex\",\n \"binaryHelp\": \"Оставьте поле пустым, чтобы использовать Codex CLI из Python SDK или найти отдельную установку. При ручной настройке укажите полный путь к codex в macOS/Linux или codex.exe в Windows. Встроенные бинарные файлы приложения ChatGPT и расширений редактора не поддерживаются. QwenPaw также читает CODEX_BINARY и PATH бэкенда.\",\n \"binaryPlaceholder\": \"Автоопределение или путь к исполняемому файлу Codex\",\n \"qoderBinary\": \"Исполняемый файл Qoder\",\n \"qoderBinaryHelp\": \"Оставьте поле пустым, чтобы использовать qodercli из Python SDK или найти установленную версию. При ручной настройке укажите полный путь к qodercli в macOS/Linux или qodercli.exe в Windows. QwenPaw также читает QODERCLI_PATH и PATH бэкенда.\",\n \"qoderBinaryPlaceholder\": \"Автоопределение или путь к qodercli\",\n \"detect\": \"Проверить\",\n \"detectedBinary\": \"Codex обнаружен\",\n \"qoderDetectedBinary\": \"Qoder обнаружен\",\n \"apiKeyLoginHint\": \"Также можно выполнить аутентификацию в терминале: codex login --with-api-key\",\n \"qoderAuthHint\": \"Подключение откроет вход Qoder в браузере. Также можно выполнить qodercli login или задать QODER_PERSONAL_ACCESS_TOKEN перед запуском бэкенда.\",\n \"qoderConnect\": \"Подключить Qoder\",\n \"model\": \"Модель агента\",\n \"modelDefault\": \"Использовать модель агента по умолчанию\",\n \"defaultBadge\": \"По умолчанию\",\n \"reasoningEffort\": \"Уровень рассуждения\",\n \"reasoningDefault\": \"Использовать значение модели по умолчанию\",\n \"codexNotFound\": \"Среда выполнения Codex не найдена. Установите qwenpaw[codex] или укажите отдельный Codex CLI.\",\n \"qoderNotFound\": \"Среда выполнения Qoder не найдена. Установите qwenpaw[qoder] или укажите qodercli.\",\n \"chatSettingsHint\": \"Выберите модель, уровень рассуждения и разрешения на выполнение на панели Chat.\",\n \"capabilityTitle\": \"Возможности QwenPaw\",\n \"inheritedSkills\": \"Навыки\",\n \"inheritedMcp\": \"Серверы MCP\",\n \"runtimeInherited\": \"Наследуются при запуске · новые сеансы\",\n \"notSupported\": \"Не поддерживается провайдером\",\n \"policyCompatibility\": \"Политика MCP\",\n \"toolAllowlistSupported\": \"Список разрешённых инструментов\",\n \"policyLimited\": \"Действуют ограничения провайдера\",\n \"chatModelHint\": \"Настройки агента · применяются со следующего хода\",\n \"appliesNextTurn\": \"Применяется со следующего хода\",\n \"approvalMode\": \"Разрешения на выполнение\",\n \"approvalPresets\": {\n \"ask\": {\n \"name\": \"Спрашивать перед изменениями\",\n \"description\": \"Разрешает изменения рабочей области и запрашивает подтверждение перед действиями с повышенными правами.\"\n },\n \"read-only\": {\n \"name\": \"Только чтение\",\n \"description\": \"Проверяет файлы без изменений.\"\n },\n \"workspace\": {\n \"name\": \"Доступ к рабочей области\",\n \"description\": \"Разрешает изменения рабочей области без подтверждения.\"\n },\n \"accept-edits\": {\n \"name\": \"Разрешать правки\",\n \"description\": \"Разрешает правки файлов, сохраняя остальные проверки.\"\n },\n \"plan\": {\n \"name\": \"Только план\",\n \"description\": \"Анализирует и планирует без изменения файлов.\"\n },\n \"auto\": {\n \"name\": \"Автоматически\",\n \"description\": \"Qoder сам определяет безопасные действия.\"\n },\n \"full-access\": {\n \"name\": \"Полный доступ\",\n \"description\": \"Разрешает неограниченное локальное выполнение без подтверждения.\"\n }\n }\n },\n \"create\": \"Создать агента\",\n \"createTitle\": \"Создать нового агента\",\n \"createSuccess\": \"Агент успешно создан\",\n \"createFailed\": \"Не удалось создать агента\",\n \"edit\": \"Редактировать\",\n \"editTitle\": \"Редактировать агента - {{name}}\",\n \"updateSuccess\": \"Агент успешно обновлён\",\n \"updateFailed\": \"Не удалось обновить агента\",\n \"delete\": \"Удалить\",\n \"deleteConfirm\": \"Подтвердить удаление агента\",\n \"deleteConfirmDesc\": \"После удаления агент станет недоступен, но файлы рабочего пространства будут сохранены\",\n \"deleteSuccess\": \"Агент успешно удалён\",\n \"deleteFailed\": \"Не удалось удалить агента\",\n \"selectAgent\": \"Выбрать агента\",\n \"defaultDisplayName\": \"Агент по умолчанию\",\n \"currentWorkspace\": \"Текущий агент\",\n \"switchSuccess\": \"Агент успешно переключён\",\n \"switchFailed\": \"Не удалось переключить агента\",\n \"loadFailed\": \"Не удалось загрузить список агентов\",\n \"loadConfigFailed\": \"Не удалось загрузить конфигурацию агента\",\n \"saveFailed\": \"Не удалось сохранить агента\",\n \"idRequired\": \"Пожалуйста, введите ID агента\",\n \"idLabel\": \"ID агента (необязательно)\",\n \"idHelp\": \"Оставьте пустым для автогенерации. Допускаются только буквы, цифры, дефисы и подчёркивания.\",\n \"idPattern\": \"ID может содержать только буквы, цифры, подчёркивания и дефисы\",\n \"idPlaceholder\": \"например: my-agent\",\n \"nameRequired\": \"Пожалуйста, введите название агента\",\n \"namePlaceholder\": \"например: Мой агент\",\n \"descriptionPlaceholder\": \"Кратко опишите назначение этого агента...\",\n \"workspaceHelp\": \"Оставьте пустым для автоматического создания в ~/.qwenpaw/workspaces/<id>\",\n \"dragHandleTooltip\": \"Перетащите, чтобы изменить порядок агентов\",\n \"reorderSuccess\": \"Порядок агентов сохранён\",\n \"reorderFailed\": \"Не удалось сохранить порядок агентов\",\n \"defaultNotEditable\": \"Агент по умолчанию не может быть отредактирован\",\n \"defaultNotDeletable\": \"Агент по умолчанию не может быть удалён\",\n \"disable\": \"Отключить\",\n \"enable\": \"Включить\",\n \"disabled\": \"Отключён\",\n \"disableConfirm\": \"Подтвердить отключение агента\",\n \"disableConfirmDesc\": \"После отключения экземпляр агента не будет запущен, но останется видимым в списке\",\n \"enableConfirm\": \"Подтвердить включение агента\",\n \"enableConfirmDesc\": \"После включения агент будет доступен для переключения\",\n \"disableSuccess\": \"Агент успешно отключён\",\n \"enableSuccess\": \"Агент успешно включён\",\n \"toggleFailed\": \"Не удалось переключить состояние агента\",\n \"enableAgent\": \"Включить {{name}}\",\n \"disableAgent\": \"Отключить {{name}}\",\n \"disabledAgents\": \"Отключённые ({{count}})\",\n \"pinnedAgents\": \"Закреплённые\",\n \"otherAgents\": \"Другие агенты\",\n \"pinned\": \"Закреплён\",\n \"defaultPinned\": \"Агент по умолчанию всегда закреплён\",\n \"longPressToPin\": \"Удерживайте для закрепления\",\n \"longPressToUnpin\": \"Удерживайте для открепления\",\n \"pinAgent\": \"Закрепить агента\",\n \"unpinAgent\": \"Открепить агента\",\n \"pinSuccess\": \"Агент закреплён\",\n \"unpinSuccess\": \"Агент откреплён\",\n \"pinFailed\": \"Не удалось изменить закрепление\",\n \"status\": {\n \"disabled\": \"Отключён\",\n \"pending\": \"Ожидает запуска\",\n \"starting\": \"Запускается\",\n \"running\": \"Работает\",\n \"failed\": \"Ошибка запуска\",\n \"waitUntilStarted\": \"Доступно после завершения запуска\"\n },\n \"defaultNotDisablable\": \"Агент по умолчанию нельзя отключить\",\n \"currentAgentDeleted\": \"Текущий агент был удалён, автоматически переключено на агента по умолчанию\",\n \"currentAgentDisabled\": \"Текущий агент был отключён, автоматически переключено на агента по умолчанию\",\n \"switchedToDefault\": \"Автоматически переключено на агента по умолчанию\",\n \"cannotSwitchToDisabled\": \"Этот агент отключён, пожалуйста, сначала включите его на странице управления\",\n \"model\": \"Модель\",\n \"modelHelp\": \"Назначьте конкретную LLM-модель этому агенту. Оставьте пустым для использования глобальной модели по умолчанию.\",\n \"modelPlaceholder\": \"Глобальная по умолчанию\",\n \"noConfiguredModels\": \"Нет настроенных моделей\",\n \"modelColumn\": \"Модель\",\n \"initialSkills\": \"Начальные навыки\",\n \"initialSkillsHelp\": \"Выберите навыки для добавления из пула. Позже можно добавить или удалить навыки.\",\n \"selectAll\": \"Все\",\n \"selectBuiltin\": \"Встроенные\",\n \"selectNone\": \"Очистить\",\n \"noPoolSkills\": \"В пуле нет доступных навыков\",\n \"addSkillsToAgent\": \"Добавить навыки\",\n \"copyTooltip\": \"Копировать конфигурацию агента\",\n \"copyDefaultTooltip\": \"Копировать, используя агента по умолчанию как шаблон\",\n \"copyTitle\": \"Копировать конфигурацию агента\",\n \"copyNameLabel\": \"Имя нового агента\",\n \"copyOptionsLabel\": \"Что копировать\",\n \"copyOptionAgentJson\": \"agent.json (обязательно)\",\n \"copyOptionAgentJsonHint\": \"Всегда копирует разобранную исходную конфигурацию и сбрасывает ID, имя, workspace и channels. Это не побайтовая копия agent.json на диске.\",\n \"copyOptionMdFiles\": \"AGENTS / SOUL / PROFILE / HEARTBEAT / BOOTSTRAP.md\",\n \"copyOptionSkills\": \"skills/ + manifest\",\n \"copyOptionJobs\": \"jobs.json\",\n \"copyOptionJobsHint\": \"Копирует только определения заданий, без истории. Новый агент начнёт выполнять их по расписанию сразу после создания.\",\n \"copySuccess\": \"Конфигурация агента успешно скопирована\",\n \"copyFailed\": \"Не удалось скопировать конфигурацию агента\"\n },\n \"checkpoints\": {\n \"nav\": \"Контрольные точки\",\n \"title\": \"Контрольные точки состояния\",\n \"auto\": \"Автоматически\",\n \"autoEnabled\": \"Автоматические точки включены\",\n \"autoDisabled\": \"Автоматические точки выключены\",\n \"refresh\": \"Обновить\",\n \"snapshot\": \"Снимок\",\n \"snapshotCreated\": \"Снимок создан\",\n \"graph\": \"Граф\",\n \"checkpoint\": \"Контрольная точка\",\n \"type\": \"Тип\",\n \"commit\": \"Коммит\",\n \"createdAt\": \"Создано\",\n \"session\": \"Сеанс\",\n \"channel\": \"Канал\",\n \"parent\": \"Родитель\",\n \"details\": \"Сведения\",\n \"search\": \"Поиск по запросу, имени, сеансу или SHA\",\n \"allTypes\": \"Все типы\",\n \"allSessions\": \"Все сеансы\",\n \"showingLatest\": \"Показано последних: {{count}}\",\n \"loadFailed\": \"Не удалось загрузить контрольные точки\",\n \"retry\": \"Повторить\",\n \"more\": \"Другие действия\",\n \"noMatches\": \"Нет точек, соответствующих фильтрам.\",\n \"empty\": \"Контрольных точек пока нет. История версий появится здесь.\",\n \"kind\": {\n \"auto\": \"Авто\",\n \"snapshot\": \"Снимок\",\n \"safety\": \"Защитная\",\n \"commit\": \"Коммит\"\n },\n \"summary\": { \"total\": \"точек\" },\n \"snapshotDialog\": {\n \"title\": \"Создать снимок\",\n \"session\": \"Сеанс\",\n \"name\": \"Имя\",\n \"placeholder\": \"Необязательное имя\"\n },\n \"gc\": {\n \"settingsAction\": \"Настройки автоочистки\",\n \"settingsTitle\": \"Настройки автоочистки\",\n \"keepCount\": \"Количество автоматических точек\",\n \"keepDays\": \"Хранение автоматических точек (дни)\",\n \"preRestoreDays\": \"Хранение снимков перед восстановлением (дни)\",\n \"settingsSaved\": \"Настройки автоочистки сохранены\",\n \"thoroughAction\": \"Полное сжатие контрольных точек\",\n \"thoroughTitle\": \"Полностью сжать контрольные точки?\",\n \"thoroughDescription\": \"Удаляет контрольные точки ({{count}}), игнорируя хранение автоматических точек. HEAD каждого сеанса сохраняется.\",\n \"thoroughConfirm\": \"Полностью сжать\",\n \"thoroughSuccess\": \"При полном сжатии удалено контрольных точек: {{count}}\",\n \"action\": \"Очистить точки\",\n \"title\": \"Очистить точки согласно политике хранения?\",\n \"description\": \"Удаляет просроченные контрольные точки ({{count}}) согласно текущей политике хранения. HEAD каждого сеанса сохраняется.\",\n \"confirm\": \"Очистить\",\n \"success\": \"Удалено: {{count}}\"\n },\n \"reset\": {\n \"action\": \"Сбросить данные\",\n \"title\": \"Сбросить все данные?\",\n \"description\": \"Все точки будут удалены навсегда, автоматическое создание будет отключено.\",\n \"confirm\": \"Сбросить\",\n \"success\": \"Данные сброшены\"\n },\n \"restore\": {\n \"action\": \"Восстановить\",\n \"title\": \"Восстановить контрольную точку\",\n \"preview\": \"Предпросмотр\",\n \"confirm\": \"Восстановить\",\n \"conversation\": \"Диалог\",\n \"memory\": \"Память\",\n \"files\": \"Файлы рабочей области\",\n \"refreshWarning\": \"Диалог изменится. После восстановления обновите его.\",\n \"selectedCount\": \"Выбрано файлов: {{count}}\",\n \"selectAll\": \"Выбрать все изменённые файлы\",\n \"delete\": \"Удалить\",\n \"restore\": \"Восстановить\",\n \"noFileChanges\": \"Изменения файлов не требуются.\",\n \"success\": \"Точка восстановлена. Обновите диалог, чтобы загрузить состояние.\"\n }\n },\n \"workspace\": {\n \"title\": \"Файлы\",\n \"workspacePath\": \"Рабочее пространство:\",\n \"noFiles\": \"Нет файлов\",\n \"coreFiles\": \"Основные файлы\",\n \"coreFilesDesc\": \"Базовые файлы с описанием персоны, идентичности и инструкций по инструментам.\",\n \"uploadTooltip\": \"Только ZIP-файлы\",\n \"uploadTooltipWithLimit\": \"Только ZIP-файлы, максимум {{limit}} МБ\",\n \"selectFile\": \"Выберите файл для редактирования\",\n \"fileContent\": \"Содержимое файла...\",\n \"uploadSuccess\": \"Файл успешно загружен\",\n \"uploadFailed\": \"Не удалось загрузить файл\",\n \"downloadPreparing\": \"Подготовка к скачиванию рабочего пространства...\",\n \"downloadSuccess\": \"Рабочее пространство успешно скачано\",\n \"downloadFailed\": \"Не удалось скачать рабочее пространство\",\n \"zipOnly\": \"Для загрузки поддерживаются только файлы .zip\",\n \"fileSizeExceeded\": \"Размер файла превышает лимит {{limit}} МБ. Текущий файл: {{size}} МБ\",\n \"systemPromptToggleTooltip\": \"Включить/выключить загрузку этого файла в системный промпт\",\n \"memoryFileWarning\": \"MEMORY.md обычно запрашивается агентом по требованию с помощью инструментов. Загрузка его в системный промпт может привести к слишком длинному контексту.\",\n \"configUpdated\": \"Конфигурация системного промпта обновлена\",\n \"configUpdateFailed\": \"Не удалось обновить конфигурацию системного промпта\",\n \"attribution\": \"Дизайн рабочего пространства частично вдохновлён проектом OpenClaw — спасибо! 🐾\",\n \"saveSuccess\": \"Сохранено успешно\",\n \"saveFailed\": \"Не удалось сохранить\"\n },\n \"skills\": {\n \"title\": \"Навыки\",\n \"description\": \"Управляйте навыками и возможностями агента.\",\n \"qwenpawManaged\": \"Под управлением QwenPaw\",\n \"qwenpawManagedHint\": \"Доступны для изменения и передаются поддерживаемым сторонним агентам при запуске.\",\n \"providerManaged\": \"Навыки стороннего агента\",\n \"providerManagedHint\": \"Обнаружены у текущего стороннего агента и доступны только для чтения.\",\n \"providerOnly\": \"Только текущий сторонний агент\",\n \"readOnly\": \"Только чтение\",\n \"noDescription\": \"Описание отсутствует.\",\n \"providerSkillDetails\": \"Сведения о Skill стороннего агента\",\n \"providerManagedDetailHint\": \"Этот Skill управляется сторонним агентом. QwenPaw может отображать и использовать его, но не изменять.\",\n \"provider\": \"Сторонний агент\",\n \"scope\": \"Область\",\n \"statusLabel\": \"Статус\",\n \"descriptionLabel\": \"Описание\",\n \"importHub\": \"Загрузить по URL\",\n \"supportedSources\": \"Поддерживаемые источники:\",\n \"importSkills\": \"Импорт навыка\",\n \"enterSkillUrl\": \"Введите URL навыка\",\n \"supportedSkillUrlSources\": \"Поддерживаемые маркетплейсы навыков:\",\n \"urlExamples\": \"Примеры URL:\",\n \"urlValid\": \"Действительный URL от {{source}}\",\n \"invalidUrl\": \"Недействительный URL\",\n \"clickToFill\": \"Нажмите, чтобы заполнить URL\",\n \"invalidSkillUrlSource\": \"Пожалуйста, убедитесь, что URL навыка из поддерживаемого маркетплейса\",\n \"type\": \"Тип\",\n \"installedFrom\": \"Установлено из\",\n \"source\": \"Источник\",\n \"path\": \"Путь\",\n \"tags\": \"Теги\",\n \"builtin\": \"Встроенный\",\n \"custom\": \"Пользовательский\",\n \"addSkill\": \"Добавить навык\",\n \"createSkill\": \"Создать навык\",\n \"viewSkill\": \"Просмотреть навык\",\n \"editSkill\": \"Редактировать навык\",\n \"skillName\": \"Название навыка\",\n \"skillContent\": \"Содержимое навыка\",\n \"pleaseInputName\": \"Пожалуйста, введите название навыка\",\n \"pleaseInputContent\": \"Пожалуйста, введите содержимое навыка\",\n \"skillNamePlaceholder\": \"например, weather_query\",\n \"contentPlaceholder\": \"[Формат]\\n---\\nname: skill_name (обязательно, строчные буквы с подчёркиванием)\\ndescription: Краткое описание (обязательно)\\n---\\n\\nРеализация навыка (формат Markdown)\\n\\n[Пример]\\n---\\nname: weather_query\\ndescription: Запрос информации о погоде для города\\n---\\n\\n## Функции\\nЗапрос данных о погоде в реальном времени.\\n\\n## Использование\\nПользователь вводит название города, возвращается информация о погоде.\",\n \"createSuccess\": \"Навык успешно создан\",\n \"createFailed\": \"Не удалось создать навык\",\n \"deleteConfirm\": \"Вы уверены, что хотите удалить этот навык?\",\n \"deleteSuccess\": \"Навык успешно удалён\",\n \"deleteFailed\": \"Не удалось удалить навык\",\n \"batchEnable\": \"Включить\",\n \"batchDisable\": \"Отключить\",\n \"batchEnableSuccess\": \"{{count}} навыков включено\",\n \"batchEnablePartial\": \"{{enabled}} включено, {{failed}} не удалось\",\n \"batchEnableFailed\": \"Не удалось выполнить пакетное включение\",\n \"batchDisableSuccess\": \"{{count}} навыков отключено\",\n \"batchDisablePartial\": \"{{disabled}} отключено, {{failed}} не удалось\",\n \"batchDisableFailed\": \"Не удалось выполнить пакетное отключение\",\n \"batchDeleteTitle\": \"Удалить {{count}} навыков?\",\n \"batchDeleteSuccess\": \"{{count}} навыков удалено\",\n \"batchDeletePartial\": \"{{deleted}} удалено, {{failed}} не удалось\",\n \"batchDeleteFailed\": \"Не удалось выполнить пакетное удаление\",\n \"selectedCount\": \"Выбрано {{count}}\",\n \"selectAll\": \"Выбрать все\",\n \"clearSelection\": \"Очистить\",\n \"updateSuccess\": \"Навык успешно обновлён\",\n \"updateFailed\": \"Не удалось обновить навык\",\n \"frontmatterRequired\": \"Навык должен начинаться и заканчиваться ---\",\n \"frontmatterNameRequired\": \"В навыке отсутствует обязательное поле: name\",\n \"frontmatterDescriptionRequired\": \"В навыке отсутствует обязательное поле: description\",\n \"editNotSupported\": \"Не удалось отредактировать навык\",\n \"editNote\": \"Подсказка: здесь можно редактировать содержимое навыка, каналы и конфигурацию.\",\n \"create\": \"Создать\",\n \"optimizeWithAI\": \"AI Оптимизация\",\n \"stopOptimize\": \"Стоп\",\n \"optimizeSuccess\": \"Навык успешно оптимизирован\",\n \"optimizeFailed\": \"Не удалось оптимизировать навык\",\n \"noContentToOptimize\": \"Нет содержимого для оптимизации\",\n \"cancelImport\": \"Отменить импорт\",\n \"importCancelled\": \"Импорт навыка отменён\",\n \"importTimeout\": \"Время ожидания импорта навыка истекло. Повторите попытку или проверьте сеть.\",\n \"uploadSkill\": \"Загрузить навык\",\n \"uploadZip\": \"Загрузить Zip\",\n \"uploadSuccess\": \"Навык успешно загружен\",\n \"uploadFailed\": \"Не удалось загрузить навык\",\n \"uploadNoChange\": \"Новые навыки не были импортированы. Возможно, они уже существуют\",\n \"zipOnly\": \"Поддерживается загрузка только .zip файлов\",\n \"fileSizeExceeded\": \"Размер файла превышает лимит {{limit}}МБ. Текущий файл: {{size}}МБ\",\n \"channels\": \"Применимые каналы\",\n \"allChannels\": \"Все\",\n \"lastUpdated\": \"Обновлено\",\n \"searchPlaceholder\": \"Фильтр по названию\",\n \"listView\": \"Список\",\n \"gridView\": \"Сетка\",\n \"refreshHint\": \"Обновить навыки с диска\",\n \"poolSync\": \"Статус синхронизации\",\n \"uploadedToPool\": \"Загружено в пул навыков\",\n \"downloadedToWorkspace\": \"Скачано в текущее рабочее пространство\",\n \"builtinUpgradeTitle\": \"Обновить встроенный навык\",\n \"builtinUpgradeContent\": \"В пуле доступна новая версия встроенного навыка «{{name}}». Перезаписать текущую версию?\",\n \"languageSwitchTitle\": \"Переключить язык навыка\",\n \"languageSwitchContent\": \"Следующие навыки имеют другую языковую версию в пуле и будут перезаписаны:\",\n \"overwriteExistingList\": \"Следующие навыки уже существуют и будут перезаписаны:\",\n \"nameConflict\": \"Обнаружен конфликт имён. Предложенное переименование заполнено.\",\n \"uploadToPool\": \"Синхронизировать с пулом навыков\",\n \"uploadToPoolHint\": \"Загрузить выбранные навыки рабочего пространства в общий локальный пул навыков\",\n \"downloadFromPool\": \"Загрузить из пула навыков\",\n \"emptyStateBadge\": \"Новые рабочие пространства начинают пустыми\",\n \"emptyStateTitle\": \"У этого агента ещё нет навыков\",\n \"emptyStateText\": \"Это нормально. Новые рабочие пространства не содержат навыков автоматически. Загрузите навыки из локального пула или создайте новый навык здесь, чтобы оснастить этого агента.\",\n \"emptyStateDownload\": \"Загрузить из пула навыков\",\n \"emptyStateCreate\": \"Создать первый навык\",\n \"selectWorkspaceSkill\": \"Выберите навык рабочего пространства\",\n \"selectPoolItem\": \"Выберите элемент пула навыков\",\n \"renameOptional\": \"Переименование (необязательно)\",\n \"renamePlaceholder\": \"Предложенное переименование появится здесь при конфликте\",\n \"renameSingleOnly\": \"Переименование доступно только при выборе ровно одного навыка.\",\n \"downloadSkillPool\": \"Скачать пул навыков\",\n \"uploadSkillPool\": \"Загрузить пул навыков\",\n \"config\": \"Конфигурация\",\n \"configPlaceholder\": \"{\\\"KEY\\\": \\\"value\\\"}\",\n \"configInvalidJson\": \"Неверный формат JSON\",\n \"configSaved\": \"Конфигурация сохранена\",\n \"loadFailed\": \"Не удалось загрузить навыки\",\n \"refreshFailed\": \"Не удалось обновить навыки\",\n \"createdSuccessfully\": \"Создано успешно\",\n \"saveFailed\": \"Не удалось сохранить\",\n \"importFailed\": \"Не удалось импортировать\",\n \"disabledSuccessfully\": \"Отключено успешно\",\n \"enabledSuccessfully\": \"Включено успешно\",\n \"operationFailed\": \"Операция не удалась\",\n \"provideUrl\": \"Пожалуйста, укажите URL навыка хаба\",\n \"validUrl\": \"Пожалуйста, введите действительный URL, начинающийся с http:// или https://\",\n \"importedSkill\": \"Импортирован навык: {{name}}\",\n \"batchOperation\": \"Пакетная операция\",\n \"exitBatch\": \"Выйти из режима\",\n \"noSearchResults\": \"Навыки, соответствующие вашему запросу, не найдены\",\n \"filterByTag\": \"Фильтр по тегу\",\n \"noTags\": \"Нет доступных тегов\",\n \"enabledSkills\": \"Активные навыки\",\n \"disabledSkills\": \"Неактивные навыки\",\n \"active\": \"активно\"\n },\n \"skillPool\": {\n \"description\": \"Общий локальный пул навыков для встроенных навыков и загрузок из рабочих пространств. Внешние импорты (ClawHub, LobeHub) — отдельная функция.\",\n \"broadcast\": \"Трансляция\",\n \"broadcastHint\": \"Транслировать навыки пула во все или выбранные рабочие пространства\",\n \"refreshHint\": \"Обновить пул навыков с диска\",\n \"deleteExternalConfirm\": \"Файлы внешнего навыка в {{path}} будут удалены с диска.\",\n \"deleteExternalBatchWarning\": \"Выбранные внешние навыки будут безвозвратно удалены с диска.\",\n \"broadcastTitle\": \"Транслировать {{name}}\",\n \"importBuiltin\": \"Импорт встроенных\",\n \"importBuiltinHint\": \"Импортировать встроенные навыки из исходников в пул\",\n \"importBuiltinAlertHint\": \"Требуют внимания {{count}} изменений встроенных навыков\",\n \"importBuiltinSuccess\": \"Встроенные навыки импортированы: {{names}}\",\n \"importBuiltinConflicts\": \"Пропущено из-за конфликтов имён: {{names}}\",\n \"importBuiltinNoChanges\": \"Нет встроенных изменений для импорта\",\n \"importBuiltinFailed\": \"Не удалось импортировать встроенные навыки\",\n \"importBuiltinConflictTitle\": \"Обнаружены конфликты встроенных навыков\",\n \"importBuiltinConflictContent\": \"Следующие встроенные навыки отличаются от текущих копий в пуле. Подтвердите, чтобы перезаписать их версией из исходников.\",\n \"sourceVersion\": \"Версия в исходниках\",\n \"currentVersion\": \"Текущая версия\",\n \"builtinLanguage\": \"Язык\",\n \"langDefault\": \"По умолчанию\",\n \"langDefaultTooltip\": \"Сохранить текущий язык каждого навыка\",\n \"langEn\": \"Английский\",\n \"langZh\": \"Китайский\",\n \"builtinLanguageChangeTitle\": \"Сменить язык встроенного навыка?\",\n \"builtinLanguageChangeContent\": \"Перезаписать копию «{{name}}» в пуле встроенной версией {{language}}?\",\n \"builtinLanguageChangeSuccess\": \"«{{name}}» переключён на {{language}}\",\n \"builtinLanguageChangeFailed\": \"Не удалось переключить язык встроенного навыка\",\n \"importStatusMissing\": \"Отсутствует в пуле\",\n \"importStatusCurrent\": \"Уже актуален\",\n \"importStatusOutdated\": \"Устарел\",\n \"importStatusLanguageSwitchTo\": \"Перезаписать версией на {{language}}\",\n \"importStatusConflict\": \"Конфликт\",\n \"builtinNoticeSummary\": \"Обнаружено {{count}} изменений встроенных навыков\",\n \"builtinNoticeLineAdded\": \"Добавлено: {{names}}\",\n \"builtinNoticeLineMissing\": \"Отсутствует: {{names}}\",\n \"builtinNoticeLineUpdated\": \"Обновлено: {{names}}\",\n \"builtinNoticeLineRemoved\": \"Удалено: {{names}}\",\n \"zipImport\": \"ZIP-импорт\",\n \"create\": \"Создать\",\n \"edit\": \"Редактировать\",\n \"delete\": \"Удалить\",\n \"descriptionLabel\": \"Описание\",\n \"tags\": \"Теги\",\n \"tagsPlaceholder\": \"Выберите или введите теги\",\n \"filterByTag\": \"Фильтр по тегу\",\n \"version\": \"Версия\",\n \"createTitle\": \"Создать элемент пула навыков\",\n \"editTitle\": \"Редактировать {{name}}\",\n \"skillNamePlaceholder\": \"Название навыка\",\n \"contentPlaceholder\": \"Содержимое SKILL.md\",\n \"nameConflict\": \"Обнаружен конфликт имён. Применено предложенное переименование.\",\n \"deletedFromPool\": \"Удалено из пула навыков\",\n \"deleteBuiltinConfirm\": \"Это удалит встроенный навык из пула. Позже его можно снова импортировать из исходников.\",\n \"deleteConfirm\": \"Это удалит навык из общего локального пула навыков.\",\n \"deleteTitle\": \"Удалить {{name}}?\",\n \"batchDeleteTitle\": \"Удалить {{count}} навыков из пула?\",\n \"batchDeleteSuccess\": \"{{count}} навыков из пула удалено\",\n \"batchDeletePartial\": \"{{deleted}} удалено, {{failed}} не удалось\",\n \"batchDeleteFailed\": \"Не удалось выполнить пакетное удаление\",\n \"imported\": \"Импортировано: {{names}}\",\n \"noNewImports\": \"Новые навыки не импортированы\",\n \"allBuiltin\": \"Все встроенные\",\n \"selectWorkspaces\": \"Транслировать в рабочее пространство\",\n \"allWorkspaces\": \"Все рабочие пространства\",\n \"optionalRename\": \"Необязательное переименование\",\n \"back\": \"Назад\",\n \"skillCount\": \"{{count}} навыков\",\n \"skillName\": \"Название навыка\",\n \"status\": \"Статус\",\n \"installedFrom\": \"Установлено из\",\n \"updateBuiltin\": \"Обновить\",\n \"updateConfirmTitle\": \"Обновить {{name}}\",\n \"updateConfirmContent\": \"Обновить до версии {{version}}? Текущая копия в пуле будет заменена последней встроенной версией.\",\n \"updateSuccess\": \"Обновлено успешно\",\n \"updateFailed\": \"Не удалось обновить\",\n \"broadcastSuccess\": \"Трансляция завершена\",\n \"broadcastFailed\": \"Не удалось выполнить трансляцию\",\n \"overwriteConfirm\": \"Перезаписать существующие навыки?\",\n \"overwriteTargetsContent\": \"Следующие существующие цели будут перезаписаны:\",\n \"builtinOverwriteTargetsContent\": \"Следующие встроенные цели имеют другую версию и будут перезаписаны:\",\n \"renameAndRetry\": \"Навык с таким именем уже существует. Использовать предложенное имя?\",\n \"multiConflictTitle\": \"Обнаружено несколько конфликтов\",\n \"multiConflictDesc\": \"Следующие навыки уже существуют. Устраните конфликты по отдельности и повторите попытку:\",\n \"singleConflictRename\": \"\\\"{{name}}\\\" уже существует. Переименовать в \\\"{{suggested}}\\\" для продолжения?\",\n \"statusUpToDate\": \"Актуально\",\n \"statusSynced\": \"Синхронизировано\",\n \"statusOutdated\": \"Устарело\",\n \"statusNotSynced\": \"Не синхронизировано\",\n \"statusConflict\": \"Конфликт\",\n \"renameEntry\": \"Новое имя для \\\"{{name}}\\\":\",\n \"builtin\": \"встроенный\",\n \"custom\": \"пользовательский\"\n },\n \"mcp\": {\n \"title\": \"MCP-клиенты\",\n \"qwenpawManaged\": \"Под управлением QwenPaw\",\n \"qwenpawManagedHint\": \"Общая конфигурация панели управления внедряется в поддерживаемые сторонние агенты во время выполнения.\",\n \"providerManaged\": \"Локальные MCP {{provider}}\",\n \"providerManagedHint\": \"Обнаружены в конфигурации стороннего агента. Доступны только для чтения и только этому агенту.\",\n \"providerOnly\": \"Только сторонний агент\",\n \"readOnly\": \"Только чтение\",\n \"create\": \"Создать клиент\",\n \"formatSupport\": \"Поддерживаемые форматы\",\n \"standardFormat\": \"Стандартный (обёртка mcpServers)\",\n \"directFormat\": \"Прямой (ключ → объект конфигурации)\",\n \"singleFormat\": \"Один клиент (плоские поля)\",\n \"emptyState\": \"MCP-клиенты пока не настроены\",\n \"loadError\": \"Не удалось загрузить MCP-клиентов\",\n \"createSuccess\": \"MCP-клиент успешно создан\",\n \"createError\": \"Не удалось создать MCP-клиент\",\n \"updateSuccess\": \"MCP-клиент успешно обновлён\",\n \"updateError\": \"Не удалось обновить MCP-клиент\",\n \"enableSuccess\": \"MCP-клиент успешно включён\",\n \"disableSuccess\": \"MCP-клиент успешно отключён\",\n \"toggleError\": \"Не удалось переключить статус MCP-клиента\",\n \"deleteConfirm\": \"Вы уверены, что хотите удалить этот MCP-клиент?\",\n \"deleteSuccess\": \"MCP-клиент успешно удалён\",\n \"deleteError\": \"Не удалось удалить MCP-клиент\",\n \"maskedFieldHint\": \"Чувствительные поля скрыты ***. Редактирование скрытых значений не поддерживается — замените полным новым значением.\",\n \"tools\": \"Инструменты и разрешения\",\n \"noTools\": \"На этом MCP сервере нет доступных инструментов\",\n \"toolsLoadError\": \"Не удалось загрузить инструменты с MCP сервера\",\n \"toolsConnecting\": \"MCP сервер подключается, попробуйте через некоторое время\",\n \"toolSchema\": \"Схема ввода\",\n \"access\": {\n \"title\": \"Доступ\",\n \"button\": \"Доступ\",\n \"summary\": \"Политика доступа MCP\",\n \"clientSection\": \"Общий доступ\",\n \"toolSection\": \"Доступ к инструменту\",\n \"default\": \"По умолчанию\",\n \"sourceType\": \"Источник\",\n \"sourceValue\": \"Значение источника\",\n \"sourceValueAllChannels\": \"Все каналы\",\n \"subjectType\": \"Объект\",\n \"subjectValue\": \"Значение объекта\",\n \"effectLabel\": \"Политика\",\n \"source\": {\n \"channel\": \"Канал\"\n },\n \"subjectTypeOption\": {\n \"all\": \"Все\",\n \"user\": \"Пользователь\"\n },\n \"sourceValuePlaceholder\": {\n \"channel\": \"console / dingtalk\"\n },\n \"subjectValuePlaceholder\": \"например, alice\",\n \"subjectValuePlaceholderSelectedSource\": \"ID пользователя в выбранном источнике\",\n \"subjectValuePlaceholderScoped\": \"ID пользователя в {{source}}\",\n \"subjectValueAll\": \"Все\",\n \"recentUserPlaceholder\": \"ID пользователя относится к выбранному источнику; введите или выберите заново\",\n \"noRecentUsers\": \"Для этого источника нет недавних пользователей; введите ID вручную\",\n \"ambiguousUserSourceWarning\": \"ID пользователя уникален только внутри источника. Выберите конкретный канал, чтобы избежать совпадений между каналами.\",\n \"unknownUserValueWarning\": \"Этого ID нет среди недавних активных пользователей. Сохранение разрешено.\",\n \"validation\": {\n \"missingUserValue\": \"Выберите конкретный источник и введите корректный ID пользователя.\",\n \"ambiguousUserSource\": \"Правила для пользователя должны указывать конкретный канал, а не все каналы.\",\n \"unknownUserValue\": \"Этого ID нет среди недавних активных пользователей. Сохранение разрешено.\"\n },\n \"addRule\": \"Добавить правило\",\n \"deleteRule\": \"Удалить правило\",\n \"noClientRules\": \"Нет правил\",\n \"noRules\": \"Нет правил, используется политика по умолчанию\",\n \"inherit\": \"Наследовать ({{effect}})\",\n \"stale\": \"Не найдено\",\n \"disabledTools\": \"Этот MCP клиент отключен; сохранённые правила доступа всё ещё можно редактировать.\",\n \"discardTitle\": \"Отменить несохранённые изменения?\",\n \"discardContent\": \"Несохранённые изменения политики доступа будут потеряны.\",\n \"loadError\": \"Не удалось загрузить политику доступа MCP\",\n \"saveSuccess\": \"Политика доступа MCP сохранена\",\n \"saveError\": \"Не удалось сохранить политику доступа MCP\",\n \"unmanagedRules\": \"{{count}} расширенных правил\",\n \"effect\": {\n \"allow\": \"Разрешить\",\n \"ask\": \"Спрашивать\",\n \"deny\": \"Запретить\"\n }\n },\n \"tab\": {\n \"json\": \"Импорт JSON\",\n \"form\": \"Режим формы\"\n },\n \"form\": {\n \"key\": \"Ключ клиента\",\n \"keyPlaceholder\": \"my-mcp-server\",\n \"keyRequired\": \"Ключ клиента обязателен\",\n \"name\": \"Отображаемое имя\",\n \"namePlaceholder\": \"My MCP Server\",\n \"nameRequired\": \"Отображаемое имя обязательно\",\n \"transport\": \"Транспорт\",\n \"url\": \"URL сервера\",\n \"urlRequired\": \"URL обязателен для удалённых MCP серверов\",\n \"command\": \"Команда\",\n \"commandRequired\": \"Команда обязательна для транспорта stdio\",\n \"args\": \"Аргументы (через пробел или с новой строки)\",\n \"description\": \"Описание\",\n \"descriptionPlaceholder\": \"Необязательное описание\",\n \"env\": \"Переменные окружения\",\n \"envPlaceholder\": \"KEY=VALUE (по одной на строку)\"\n },\n \"oauth\": {\n \"enableOAuth\": \"Использовать OAuth авторизацию\",\n \"manage\": \"OAuth\",\n \"authorized\": \"Авторизован\",\n \"notAuthorized\": \"Не авторизован\",\n \"expired\": \"Токен истёк\",\n \"failed\": \"Ошибка авторизации\",\n \"waiting\": \"Ожидание авторизации...\",\n \"authorize\": \"Авторизовать\",\n \"reauthorize\": \"Переавторизовать\",\n \"revoke\": \"Отозвать\",\n \"startFailed\": \"Не удалось запустить OAuth\",\n \"authFailed\": \"OAuth авторизация не удалась\",\n \"popupBlocked\": \"Всплывающее окно заблокировано браузером. Разрешите всплывающие окна и повторите попытку.\",\n \"windowClosed\": \"Окно авторизации закрыто. Повторите попытку.\",\n \"noUrl\": \"Сначала введите URL сервера\",\n \"noClientKey\": \"Сначала создайте клиент\",\n \"showAdvanced\": \"Дополнительные параметры\",\n \"hideAdvanced\": \"Скрыть параметры\",\n \"clientId\": \"Client ID (необязательно)\",\n \"clientIdPlaceholder\": \"Оставьте пустым для динамической регистрации\",\n \"scope\": \"Scope (необязательно)\",\n \"scopePlaceholder\": \"openid profile email\",\n \"authEndpoint\": \"Authorization Endpoint (переопределить)\",\n \"tokenEndpoint\": \"Token Endpoint (переопределить)\",\n \"endpointHint\": \"Оставьте пустым для автообнаружения. Заполните для ручного указания.\"\n }\n },\n \"heartbeat\": {\n \"title\": \"Пульс\",\n \"description\": \"Запускайте HEARTBEAT.md с фиксированным интервалом для самопроверок. По умолчанию выполняется тихо и не влияет на текущие диалоги, либо может отправлять ответы в последний канал чата.\",\n \"enabled\": \"Включить пульс\",\n \"every\": \"Интервал\",\n \"everyRequired\": \"Обязательно\",\n \"everyMin\": \"Должно быть не меньше 1\",\n \"timeoutSeconds\": \"Таймаут выполнения (секунды)\",\n \"timeoutRequired\": \"Обязательно\",\n \"timeoutMin\": \"Должно быть не меньше 1\",\n \"timeoutMax\": \"Должно быть не больше 3600\",\n \"unitMinutes\": \"Минуты\",\n \"unitHours\": \"Часы\",\n \"target\": \"Цель ответа\",\n \"targetMain\": \"Тихий режим (по умолчанию, без вывода в канал)\",\n \"targetLast\": \"Отправлять в последний канал чата\",\n \"activeHours\": \"Активные часы (необязательно)\",\n \"activeStart\": \"Время начала\",\n \"activeEnd\": \"Время окончания\",\n \"loadFailed\": \"Не удалось загрузить конфигурацию пульса\",\n \"saveSuccess\": \"Успешно сохранено; пульс перезагружен на лету\",\n \"saveFailed\": \"Не удалось сохранить конфигурацию пульса\",\n \"targetInbox\": \"Отправлять во входящие\",\n \"runNow\": \"Запустить сейчас\",\n \"runNowSuccess\": \"Пульс запущен. Через несколько секунд проверьте входящие или целевой канал.\",\n \"runNowFailed\": \"Не удалось запустить выполнение пульса\"\n },\n \"cronJobs\": {\n \"title\": \"Cron-задачи\",\n \"description\": \"Создавайте и управляйте задачами по расписанию, которые автоматически выполняются в указанное время.\",\n \"createJob\": \"Создать задачу\",\n \"editJob\": \"Редактировать задачу\",\n \"confirmDelete\": \"Подтвердить удаление\",\n \"deleteConfirm\": \"Вы уверены, что хотите удалить эту Cron Job?\",\n \"okText\": \"OK\",\n \"cancelText\": \"Отмена\",\n \"deleteText\": \"Удалить\",\n \"id\": \"ID задачи\",\n \"name\": \"Название задачи\",\n \"enabled\": \"Статус\",\n \"scheduleType\": \"Тип расписания\",\n \"scheduleCron\": \"Расписание (Cron)\",\n \"scheduleCronLabel\": \"Расписание (Cron)\",\n \"scheduleTimezone\": \"Часовой пояс\",\n \"cronType\": \"Частота\",\n \"cronTypeHourly\": \"Каждый час\",\n \"cronTypeDaily\": \"Ежедневно\",\n \"cronTypeWeekly\": \"Еженедельно\",\n \"cronTypeCustom\": \"Пользовательский\",\n \"cronTime\": \"Время\",\n \"cronDaysOfWeek\": \"Дни недели\",\n \"cronDayMon\": \"Понедельник\",\n \"cronDayTue\": \"Вторник\",\n \"cronDayWed\": \"Среда\",\n \"cronDayThu\": \"Четверг\",\n \"cronDayFri\": \"Пятница\",\n \"cronDaySat\": \"Суббота\",\n \"cronDaySun\": \"Воскресенье\",\n \"cronCustomExpression\": \"Cron-выражение\",\n \"taskText\": \"Текст сообщения\",\n \"action\": \"Действие\",\n \"disable\": \"Отключить\",\n \"executeNow\": \"Выполнить сейчас\",\n \"edit\": \"Редактировать\",\n \"delete\": \"Удалить\",\n \"totalItems\": \"Всего {{count}} элементов\",\n \"perPage\": \"/ стр.\",\n \"pleaseInputId\": \"Пожалуйста, введите ID задачи\",\n \"pleaseInputName\": \"Пожалуйста, введите название задачи\",\n \"pleaseInputCron\": \"Пожалуйста, введите cron-выражение\",\n \"pleaseSelectTaskType\": \"Пожалуйста, выберите тип задачи\",\n \"pleaseInputRequest\": \"Пожалуйста, введите содержимое запроса\",\n \"pleaseInputMessageContent\": \"Пожалуйста, введите текст сообщения\",\n \"pleaseInputChannel\": \"Пожалуйста, введите целевой канал\",\n \"pleaseInputUserId\": \"Пожалуйста, введите ID целевого пользователя\",\n \"pleaseInputSessionId\": \"Пожалуйста, введите ID целевой сессии\",\n \"jobIdPlaceholder\": \"например, daily-report-job\",\n \"jobNamePlaceholder\": \"например, Ежедневный утренний отчёт\",\n \"selectTimezone\": \"Выберите часовой пояс\",\n \"taskDescriptionPlaceholder\": \"Для текстовых задач введите текст сообщения...\",\n \"invalidJsonFormat\": \"Неверный формат JSON\",\n \"jsonFormatRequired\": \"Требуется формат JSON\",\n \"taskType\": \"Тип задачи\",\n \"text\": \"Текст сообщения\",\n \"requestInput\": \"Содержимое запроса\",\n \"requestSessionId\": \"ID сессии запроса\",\n \"requestUserId\": \"ID пользователя запроса\",\n \"dispatchChannel\": \"Целевой канал\",\n \"dispatchTargetUserId\": \"ID целевого пользователя\",\n \"dispatchTargetSessionId\": \"ID целевой сессии\",\n \"dispatchMode\": \"Режим доставки\",\n \"silentDelivery\": \"Тихая доставка\",\n \"runtimeShareSession\": \"Общая сессия\",\n \"runtimeMaxConcurrency\": \"Макс. параллельность\",\n \"runtimeTimeoutSeconds\": \"Таймаут (секунды)\",\n \"runtimeMisfireGraceSeconds\": \"Льготный период пропуска (секунды)\",\n \"runtimeToolSafety\": \"Проверка безопасности инструментов\",\n \"idTooltip\": \"Генерируемый системой, нередактируемый уникальный UUID, присваиваемый при создании задачи.\",\n \"nameTooltip\": \"Понятное имя, чтобы вам было проще найти эту задачу.\",\n \"cronTooltip\": \"Определите, когда задача должна запускаться\",\n \"cronExample\": \"Популярные примеры: '0 9 * * *' = ежедневно в 09:00 | '*/30 * * * *' = каждые 30 мин | '0 */2 * * *' = каждые 2 часа | '0 0 * * 0' = воскресенье, полночь\",\n \"cronHelper\": \"Впервые работаете с cron-выражениями?\",\n \"cronHelperLink\": \"Использовать онлайн-генератор\",\n \"timezoneTooltip\": \"Часовой пояс для cron-расписания. По умолчанию: UTC\",\n \"taskTypeTooltip\": \"Выберите 'text' для простых текстовых задач или 'agent' для сложных сценариев с агентом.\",\n \"textTooltip\": \"Простая текстовая задача: здесь указывается фактический текст сообщения. Обязательно при типе задачи «text».\",\n \"requestInputTooltip\": \"Содержимое сообщения в формате JSON. Именно это агент получит и обработает. Обязательно при типе задачи «agent».\",\n \"requestInputExample\": \"Формат: [{\\\"role\\\":\\\"user\\\",\\\"content\\\":[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Ваше сообщение\\\"}]}]\",\n \"requestSessionIdTooltip\": \"ID сессии для контекста запроса. Если не уверены, используйте 'default'.\",\n \"requestUserIdTooltip\": \"ID пользователя, который инициирует запрос. Для автоматических задач используйте 'system'.\",\n \"dispatchChannelTooltip\": \"Целевой канал, куда будет отправлен ответ (например, 'console', 'discord', 'imessage').\",\n \"dispatchTargetUserIdTooltip\": \"ID пользователя, который получит ответ в целевом канале.\",\n \"dispatchTargetSessionIdTooltip\": \"ID сессии, куда будет доставлен ответ в целевом канале.\",\n \"dispatchModeTooltip\": \"Выберите 'stream' для ответов в реальном времени или 'final' только для итоговых ответов.\",\n \"silentDeliveryTooltip\": \"Выполнить задачу агента с сохранением сессии и трассировки, но не отправлять результат в канал.\",\n \"shareSessionTooltip\": \"При включении используется общая сессия с целевым пользователем. При отключении каждый запуск создает изолированный контекст с уникальным ID. Подходит для независимых задач без памяти. По умолчанию: включено\",\n \"maxConcurrencyTooltip\": \"Максимальное количество одновременно выполняемых экземпляров этой задачи. По умолчанию: 1\",\n \"timeoutSecondsTooltip\": \"Максимальное время выполнения в секундах. При превышении задача будет остановлена.\",\n \"misfireGraceSecondsTooltip\": \"Льготный период для пропущенных запусков. Если задача пропустила время запуска больше чем на это значение, она не будет выполнена.\",\n \"toolSafetyTooltip\": \"При включении рискованные вызовы инструментов требуют одобрения пользователя (может блокировать автоматические задачи). При выключении все инструменты выполняются без одобрения. По умолчанию: выключено\",\n \"executeNowTitle\": \"Выполнить задачу сейчас\",\n \"executeNowContent\": \"Вы уверены, что хотите выполнить «{{name}}» прямо сейчас?\",\n \"executeNowConfirm\": \"Выполнить сейчас\",\n \"createFromTemplate\": \"Создать из шаблона\",\n \"templateModalTitle\": \"Выберите шаблон задачи\",\n \"templateModalDescription\": \"Выберите встроенный шаблон и при необходимости измените ключевые параметры на панели перед сохранением.\",\n \"useTemplate\": \"Использовать шаблон\",\n \"templateTagPersonal\": \"Личное\",\n \"templateTagTeam\": \"Команда\",\n \"templateTagReminder\": \"Напоминание\",\n \"templateTagCalendar\": \"Календарь\",\n \"templateTagCalendarRecommended\": \"Рекомендуется для календаря\",\n \"saveResultToInbox\": \"Сохранять результат во входящих\",\n \"saveResultToInboxTooltip\": \"Если включено, успешные выполнения с успешной доставкой сохраняются во входящих. Ошибки доставки всегда записываются во входящие как резерв.\",\n \"scheduleFilterAll\": \"Все типы расписания\",\n \"scheduleTypeRecurring\": \"Повторяющаяся\",\n \"scheduleTypeOnce\": \"Запланированная\",\n \"onceRunAt\": \"Запуск в\",\n \"repeatEnabled\": \"Повтор\",\n \"repeatEnabledTooltip\": \"Включить повторяющееся расписание от этого времени начала\",\n \"repeatFrequency\": \"Частота повтора\",\n \"repeatEveryDays\": \"Повторять каждые (дни)\",\n \"repeatEveryPrefix\": \"Каждые\",\n \"repeatEverySuffix\": \"дн.\",\n \"repeatEndType\": \"Окончание повтора\",\n \"repeatEndNever\": \"Никогда\",\n \"repeatEndUntil\": \"До даты\",\n \"repeatEndCount\": \"Ограниченное число запусков\",\n \"repeatUntil\": \"Дата окончания\",\n \"repeatCount\": \"Число запусков\",\n \"pleaseSelectScheduleType\": \"Выберите тип расписания\",\n \"pleaseInputRunAt\": \"Выберите время запуска\",\n \"pleaseInputRepeatEveryDays\": \"Введите интервал повтора (дни)\",\n \"pleaseSelectRepeatEndType\": \"Выберите условие окончания\",\n \"pleaseInputRepeatUntil\": \"Выберите дату окончания\",\n \"pleaseInputRepeatCount\": \"Введите число запусков\",\n \"validation\": {\n \"cronRequired\": \"Для повторяющегося расписания требуется cron-выражение.\",\n \"runAtRequired\": \"Для одноразового расписания требуется время выполнения.\",\n \"repeatUntilRequired\": \"При выборе «До даты» необходимо указать дату окончания.\",\n \"repeatCountRequired\": \"При выборе «Ограниченное число запусков» необходимо указать количество запусков.\",\n \"repeatUntilAfterRunAt\": \"Дата окончания должна быть позже времени выполнения. Выберите дату окончания после времени выполнения.\",\n \"textRequired\": \"При типе задачи text содержимое сообщения обязательно.\",\n \"requestRequired\": \"При типе задачи agent содержимое запроса обязательно.\",\n \"invalidCronExpression\": \"Некорректное cron-выражение. Используйте формат из 5 полей.\"\n },\n \"listView\": \"Список\",\n \"calendarView\": \"Календарь\",\n \"calendarEmptyHint\": \"Запланированных задач пока нет. Создайте задачи по расписанию, чтобы увидеть их в календаре.\",\n \"calendarMoreItems\": \"+ещё {{count}}\",\n \"calendarDayListTitle\": \"Запланированные задачи на {{date}}\",\n \"executionHistory\": \"История\",\n \"historyTitle\": \"История — {{name}}\",\n \"historyEmpty\": \"Записей о выполнении пока нет.\",\n \"historyStatusSuccess\": \"Успех\",\n \"historyStatusFailed\": \"Ошибка\",\n \"historyStatusRunning\": \"Выполняется\",\n \"historyStatusCancelled\": \"Отменено\",\n \"historyTriggerScheduled\": \"Запуск по расписанию\",\n \"historyTriggerManual\": \"Ручной запуск\",\n \"historyExpand\": \"Развернуть\",\n \"historyCollapse\": \"Свернуть\",\n \"templates\": {\n \"dailyTechNewsBrief\": {\n \"title\": \"Дайджест технических новостей\",\n \"description\": \"В 09:30 в будни собирать актуальные технические заголовки в краткий обзор.\",\n \"frequency\": \"Будни 09:30 (30 9 * * 1-5)\"\n },\n \"dailyEveningReflection\": {\n \"title\": \"Вечерняя рефлексия\",\n \"description\": \"В 20:00 в будни — короткая подсказка подвести итоги рабочего дня.\",\n \"frequency\": \"Будни 20:00 (0 20 * * 1-5)\"\n },\n \"weekendRelaxationReminder\": {\n \"title\": \"Напоминание отдохнуть на выходных\",\n \"description\": \"По утрам в выходные — подборка популярных фильмов для лёгкого отдыха.\",\n \"frequency\": \"Выходные 10:00 (0 10 * * 6,0)\"\n },\n \"pomodoroBreakReminder\": {\n \"title\": \"Напоминание о перерыве (помодоро)\",\n \"description\": \"В рабочие часы каждые 25 минут напоминать попить воды или дать глазам отдохнуть.\",\n \"frequency\": \"Будни 09:00–17:59 каждые 25 мин (*/25 9-17 * * 1-5)\"\n },\n \"petCareReminder\": {\n \"title\": \"Питомец: глистогонное / прививка\",\n \"description\": \"15-го числа каждого месяца вечером — напоминание о дегельминтизации или следующей прививке.\",\n \"frequency\": \"Ежемесячно 15-го в 20:00 (0 20 15 * *)\"\n },\n \"onceTextBirthdayReminder\": {\n \"title\": \"Напоминание о дне рождения\",\n \"description\": \"Напомнить, что у xx день рождения 1 января.\",\n \"frequency\": \"Выполнить: 2026-01-01 09:00\"\n },\n \"onceAgentBusinessTripPrep\": {\n \"title\": \"Подготовка к командировке\",\n \"description\": \"Проверить погоду в пункте назначения и предложить рекомендации по подготовке к поездке.\",\n \"frequency\": \"Выполнить: 2026-01-01 20:00\"\n },\n \"repeatCountTextMedicineReminder\": {\n \"title\": \"Напоминание о лекарствах (14 раз)\",\n \"description\": \"Напоминать вовремя принимать лекарства в рамках курса из 14 запусков.\",\n \"frequency\": \"Первый запуск: 2026-01-01 09:00 | Каждые 1 день | 14 запусков\"\n },\n \"repeatCountAgentDietPlan\": {\n \"title\": \"План питания на 14 дней\",\n \"description\": \"Генерировать и отправлять ежедневные рекомендации по питанию в течение 14 дней.\",\n \"frequency\": \"Первый запуск: 2026-01-01 08:00 | Каждые 1 день | 14 запусков\"\n },\n \"repeatUntilTextWeeklyMeeting\": {\n \"title\": \"Напоминание о еженедельной встрече (2 месяца)\",\n \"description\": \"Еженедельно напоминать о встрече до установленной даты окончания.\",\n \"frequency\": \"Первый запуск: 2026-01-02 08:45 | Каждые 7 дней | До 2026-03-01\"\n },\n \"repeatUntilAgentWeeklySummary\": {\n \"title\": \"Недельный итог перед встречей\",\n \"description\": \"Перед каждой недельной встречей формировать рабочий итог на основе memory за последнюю неделю.\",\n \"frequency\": \"Первый запуск: 2026-01-02 08:30 | Каждые 7 дней | До 2026-03-01\"\n }\n }\n },\n \"channels\": {\n \"title\": \"Каналы\",\n \"description\": \"Управление и настройка каналов сообщений\",\n \"loading\": \"Загрузка каналов...\",\n \"configSaved\": \"Конфигурация успешно сохранена\",\n \"configFailed\": \"Не удалось сохранить конфигурацию\",\n \"channelType\": \"Тип канала\",\n \"status\": \"Статус\",\n \"totalItems\": \"Всего {{count}} элементов\",\n \"botPrefix\": \"Префикс бота\",\n \"showToolCalls\": \"Показывать сведения о вызове инструмента\",\n \"showToolCallsTooltip\": \"Показывать пользователям сведения о вызове инструмента (выключите, чтобы скрыть)\",\n \"showToolResults\": \"Показывать сведения о результате инструмента\",\n \"showToolResultsTooltip\": \"Показывать пользователям сведения о результате инструмента (выключите, чтобы скрыть)\",\n \"toolCallMaxLength\": \"Максимальная длина отображения вызова инструмента\",\n \"toolResultMaxLength\": \"Максимальная длина отображения результата инструмента\",\n \"toolMaxLengthTooltip\": \"Установите 0, чтобы не ограничивать длину\",\n \"showThinking\": \"Показывать рассуждения\",\n \"showThinkingTooltip\": \"Показывать пользователям рассуждения модели (выключите, чтобы скрыть)\",\n \"noTextDebounce\": \"Дебаунс без текста\",\n \"noTextDebounceTooltip\": \"Если включено, сообщения только с медиа (изображения/видео/файлы) буферизуются до прихода текстового сообщения, затем объединяются. Отключите для немедленной обработки всех сообщений. Голосовые сообщения всегда обрабатываются немедленно.\",\n \"streamingEnabled\": \"Потоковый вывод\",\n \"streamingEnabledDingtalkHint\": \"Действует только при типе сообщения Card\",\n \"streamingEnabledFeishuHint\": \"Необходимо включить разрешение cardkit:card:write на странице управления разрешениями открытой платформы Feishu\",\n \"ackMessage\": \"Сообщение подтверждения\",\n \"ackMessageTooltip\": \"Отправить мгновенный ответ при получении сообщения до начала обработки агентом. Оставьте пустым для отключения.\",\n \"ackMessagePlaceholder\": \"⏳ Обработка...\",\n \"notSet\": \"Не задано\",\n \"clickCardToEdit\": \"Нажмите на карточку для редактирования\",\n \"settings\": \"Настройки\",\n \"channelSettings\": \"Настройки канала\",\n \"pleaseInputDbPath\": \"Пожалуйста, введите путь к БД\",\n \"pleaseInputPollInterval\": \"Пожалуйста, введите интервал опроса\",\n \"discordBotToken\": \"Токен Discord-бота\",\n \"acceptBotMessages\": \"Принимать сообщения от ботов\",\n \"acceptBotMessagesTooltip\": \"При включении будут приниматься сообщения от других ботов\",\n \"httpProxyPlaceholder\": \"http://127.0.0.1:18118\",\n \"httpProxyAuthPlaceholder\": \"user:password\",\n \"dbPathPlaceholder\": \"~/Library/Messages/chat.db\",\n \"botPrefixPlaceholder\": \"@bot\",\n \"dmPolicy\": \"Политика личных сообщений\",\n \"dmPolicyTooltip\": \"Кто может взаимодействовать с ботом в личных сообщениях. Open: все; Allowlist: только авторизованные пользователи\",\n \"groupPolicy\": \"Политика групп\",\n \"groupPolicyTooltip\": \"Кто может взаимодействовать с ботом в групповых чатах. Open: все; Allowlist: только авторизованные пользователи\",\n \"requireMention\": \"Требовать @упоминание\",\n \"requireMentionTooltip\": \"Бот отвечает в групповых чатах только при явном @упоминании\",\n \"policyOpen\": \"Open\",\n \"policyAllowlist\": \"Allowlist\",\n \"allowFrom\": \"Пользователи allowlist\",\n \"allowFromTooltip\": \"Список ID пользователей, которым разрешено взаимодействовать с ботом. Включите белый список, затем напишите боту в ЛС — ваш ID будет в сообщении об отказе\",\n \"allowFromPlaceholder\": \"Введите ID пользователя и нажмите Enter, чтобы добавить\",\n \"denyMessage\": \"Сообщение об отказе\",\n \"denyMessageTooltip\": \"Пользовательское сообщение для пользователей, которым отказан доступ\",\n \"twilioAccountSid\": \"Twilio Account SID\",\n \"twilioAuthToken\": \"Twilio Auth Token\",\n \"phoneNumber\": \"Номер телефона\",\n \"phoneNumberSid\": \"Phone Number SID\",\n \"phoneNumberSidHelp\": \"Можно найти в разделе Phone Numbers → Active Numbers в Twilio Console.\",\n \"ttsProvider\": \"TTS-провайдер\",\n \"ttsVoice\": \"TTS-голос\",\n \"sttProvider\": \"STT-провайдер\",\n \"language\": \"Язык\",\n \"welcomeGreeting\": \"Приветственное сообщение\",\n \"voiceSetupGuide\": \"Создайте аккаунт Twilio, купите номер телефона и затем введите данные ниже. Account SID и Auth Token находятся на панели Twilio Console. Phone Number SID указан в разделе Phone Numbers → Active Numbers.\",\n \"voiceSetupLink\": \"Открыть Twilio Console\",\n \"sipSetupGuide\": \"Настройте SIP-регистратор (например, Asterisk, FreeSWITCH) и введите учётные данные SIP ниже. В режиме Dev pyVoIP обрабатывает SIP/RTP локально. В режиме Production используется LiveKit SIP Server.\",\n \"sipMode\": \"Режим SIP\",\n \"sipModeTooltip\": \"Dev: Python pyVoIP для локального тестирования. Production: LiveKit SIP Server для высокой нагрузки.\",\n \"sipServer\": \"SIP-сервер\",\n \"sipServerPlaceholder\": \"Оставьте пустым для встроенного регистратора\",\n \"sipServerPlaceholderLivekit\": \"Не требуется для режима LiveKit\",\n \"sipUsername\": \"Имя пользователя SIP\",\n \"sipPassword\": \"Пароль SIP\",\n \"sipPort\": \"Порт SIP\",\n \"sipTransport\": \"Транспорт\",\n \"sipDashscopeApiKey\": \"DashScope API Key\",\n \"sipDashscopeApiKeyTooltip\": \"API-ключ для Aliyun DashScope STT/TTS. Если пусто, используется переменная окружения DASHSCOPE_API_KEY.\",\n \"livekitUrl\": \"LiveKit URL\",\n \"livekitApiKey\": \"LiveKit API Key\",\n \"livekitApiSecret\": \"LiveKit API Secret\",\n \"livekitSipTrunkId\": \"LiveKit SIP Trunk ID\",\n \"livekitRoomName\": \"Имя комнаты LiveKit\",\n \"livekitRoomNameTooltip\": \"Имя комнаты LiveKit для ожидания SIP-вызовов. Должно совпадать с именем комнаты в правиле SIP Dispatch Rule.\",\n \"loginWeCom\": \"Получить QR-код WeCom\",\n \"wecomSetupGuide\": \"При создании бота через QR-код, на этапе авторизации возможностей бота выберите «Пропустить авторизацию». Если выбрать «Подтвердить авторизацию», другие пользователи, кроме создателя бота, могут не иметь возможности взаимодействовать с ботом.\",\n \"wecomScanAuth\": \"Сканировать для авторизации\",\n \"wecomAuthHint\": \"Отсканируйте QR-код выше с помощью WeCom для авторизации. Bot ID и Secret будут заполнены автоматически.\",\n \"wecomAuthSuccess\": \"Бот WeCom авторизован. Bot ID и Secret заполнены автоматически.\",\n \"wecomQrcodeFailed\": \"Не удалось получить QR-код WeCom. Попробуйте ещё раз.\",\n \"slackBotToken\": \"Bot Token\",\n \"slackBotTokenTooltip\": \"Slack Bot User OAuth Token, начинается с xoxb-\",\n \"slackAppToken\": \"App Token\",\n \"slackAppTokenTooltip\": \"Slack App-Level Token для Socket Mode, начинается с xapp-\",\n \"slackProxyTooltip\": \"URL HTTP-прокси для подключения к Slack API\",\n \"dingtalkSetupGuide\": \"Отсканируйте QR-код в DingTalk для мгновенного создания бота. Приложение DingTalk будет создано автоматически, а Client ID и Client Secret будут заполнены.\",\n \"dingtalkScanAuth\": \"Сканировать для создания бота\",\n \"dingtalkGetQrcode\": \"Получить QR-код DingTalk\",\n \"dingtalkScanHint\": \"Отсканируйте QR-код выше с помощью DingTalk. Client ID и Client Secret будут заполнены автоматически после авторизации.\",\n \"dingtalkAuthSuccess\": \"Бот DingTalk создан. Client ID и Client Secret заполнены автоматически.\",\n \"dingtalkQrcodeFailed\": \"Не удалось получить QR-код DingTalk. Попробуйте ещё раз.\",\n \"dingtalkQrcodeExpired\": \"QR-код истёк. Получите новый.\",\n \"qqSetupGuide\": \"Отсканируйте QR-код с помощью QQ, чтобы авторизовать бота. После сканирования APP ID и Client Secret будут автоматически получены и введены в поле.\",\n \"qqScanAuth\": \"Отсканируйте штрихкод, чтобы авторизовать бота\",\n \"qqGetQrcode\": \"Получить QR-код QQ\",\n \"qqScanHint\": \"Отсканируйте QR-код выше с помощью QQ. После завершения авторизации APP ID и Client Secret будут автоматически введены в поле.\",\n \"qqAuthSuccess\": \"Авторизация бота QQ прошла успешно, APP ID и Client Secret уже введены в поле.\",\n \"qqQrcodeFailed\": \"Не удалось получить QR-код QQ. Попробуйте позже.\",\n \"qqQrcodeExpired\": \"Срок действия QR-кода истек. Пожалуйста, получите новый.\",\n \"atSenderOnReply\": \"@ отправителя при ответе\",\n \"atSenderOnReplyTooltip\": \"При включении бот будет упоминать отправителя в первом сообщении ответа в групповых чатах. Примечание: в режиме карточек @упоминание работает только для внутренних сотрудников организации.\",\n \"dingtalkEndpoint\": \"API Endpoint\",\n \"dingtalkEndpointTooltip\": \"Пользовательский API-адрес для приватного развёртывания DingTalk. Оставьте пустым для использования официального адреса по умолчанию.\",\n \"wecomCancelled\": \"Авторизация отменена\",\n \"wecomAuthFailed\": \"Ошибка авторизации: {{msg}}\",\n \"filterAll\": \"Все\",\n \"builtin\": \"Встроенные\",\n \"custom\": \"Пользовательские\",\n \"feishuRegion\": \"Регион\",\n \"feishuRegionTooltip\": \"Выберите Feishu для Китая или Lark для международного использования\",\n \"feishuChina\": \"Feishu (Китай)\",\n \"feishuInternational\": \"Lark (Международный)\",\n \"feishuScanGuide\": \"Отсканируйте QR-код в Feishu для мгновенного создания бота. Приложение Feishu будет создано автоматически, а App ID и App Secret будут заполнены.\",\n \"feishuScanLogin\": \"Сканировать для создания бота\",\n \"feishuGetQrcode\": \"Получить QR-код Feishu\",\n \"feishuScanHint\": \"Отсканируйте QR-код выше с помощью Feishu. App ID и App Secret будут заполнены автоматически после авторизации.\",\n \"feishuAuthSuccess\": \"Бот Feishu создан. App ID и App Secret заполнены автоматически.\",\n \"feishuQrcodeExpired\": \"QR-код истёк. Получите новый.\",\n \"feishuQrcodeFailed\": \"Не удалось получить QR-код Feishu. Попробуйте ещё раз.\",\n \"wechatSetupGuide\": \"Бот для личного аккаунта WeChat (протокол iLink). При первом запуске без Bot Token будет выведена ссылка на QR-код для авторизации. Токен сохраняется локально для дальнейшего использования.\",\n \"wechatContextTokenLimit\": \"Ограничение платформы WeChat iLink: context_token каждого сообщения пользователя позволяет отправить максимум 10 ответных сообщений. Это жёсткое ограничение платформы. Рекомендуется отключить вывод размышлений и инструментов или включить функцию объединения сообщений, чтобы избежать превышения лимита и ошибок отправки.\",\n \"wechatBotToken\": \"Bot Token\",\n \"wechatBotTokenTooltip\": \"Bearer-токен, полученный после входа по QR-коду. Оставьте пустым для запуска процедуры входа при старте.\",\n \"wechatBotTokenPlaceholder\": \"Заполняется автоматически после QR-входа или введите вручную\",\n \"wechatBotTokenFile\": \"Путь к файлу токена\",\n \"wechatBotTokenFileTooltip\": \"Путь для сохранения Bot Token. По умолчанию: ~/.qwenpaw/wechat_bot_token\",\n \"wechatMediaDir\": \"Директория медиафайлов\",\n \"wechatMessageMerge\": \"Объединение сообщений\",\n \"wechatMessageMergeTooltip\": \"Объединяет несколько ответных сообщений для отправки меньшим количеством, чтобы не превышать лимит WeChat в 10 сообщений на context_token. При задержке 0 все текстовые ответы объединяются в одно сообщение в конце запроса. Положительная задержка (в мс) объединяет соседние сообщения в указанном временном окне.\",\n \"wechatMessageMergeDelayMs\": \"Задержка объединения (мс)\",\n \"wechatMessageMergeDelayMsTooltip\": \"Временное окно для объединения соседних сообщений. Значение 0 объединяет все сообщения в один финальный ответ. Положительное значение (напр. 1000) буферизует сообщения в течение указанного периода перед отправкой.\",\n \"wechatMessageMergeDelayMsValidation\": \"Введите допустимое неотрицательное целое число\",\n \"wechatScanLogin\": \"Войти через QR-код\",\n \"wechatGetQrcode\": \"Получить QR-код для входа\",\n \"wechatScanHint\": \"Отсканируйте QR-код в WeChat, затем нажмите Сохранить\",\n \"wechatQrcodeFailed\": \"Не удалось получить QR-код, попробуйте снова\",\n \"wechatLoginSuccess\": \"Вход через WeChat выполнен, токен заполнен\",\n \"wechatQrcodeExpired\": \"QR-код истёк, получите новый\",\n \"shareSessionInGroup\": \"Общий контекст в группе\",\n \"shareSessionInGroupTooltip\": \"Если включено, все участники группы используют общий контекст разговора. Если выключено, у каждого участника независимый контекст.\",\n \"accessControlEnabled\": \"Контроль доступа\",\n \"accessControlEnabledTooltip\": \"При включении только пользователи из белого списка могут взаимодействовать с ботом. Неизвестным пользователям отображается их ID и они добавляются в список ожидания.\",\n \"accessControlDm\": \"Контроль доступа в ЛС\",\n \"accessControlDmTooltip\": \"При включении только пользователи из белого списка могут общаться с ботом в личных сообщениях\",\n \"accessControlGroup\": \"Контроль доступа в группах\",\n \"accessControlGroupTooltip\": \"При включении только пользователи из белого списка могут общаться с ботом в групповых чатах\",\n \"dmDisabled\": \"Отключить ЛС\",\n \"dmDisabledTooltip\": \"При включении бот будет полностью игнорировать все личные сообщения\",\n \"groupDisabled\": \"Отключить группы\",\n \"groupDisabledTooltip\": \"При включении бот будет полностью игнорировать все групповые сообщения\",\n \"manageAccessControl\": \"Контроль доступа\",\n \"pendingApprovals\": \"Ожидают одобрения\",\n \"whitelist\": \"Белый список\",\n \"blacklist\": \"Чёрный список\",\n \"addUser\": \"Добавить\",\n \"addUserPlaceholder\": \"Введите ID пользователя\",\n \"userId\": \"ID пользователя\",\n \"username\": \"Имя пользователя\",\n \"channel\": \"Канал\",\n \"firstMessage\": \"Первое сообщение\",\n \"time\": \"Время\",\n \"actions\": \"Действия\",\n \"approve\": \"Белый список\",\n \"deny\": \"Чёрный список\",\n \"dismiss\": \"Отклонить\",\n \"noWhitelistUsers\": \"Нет пользователей в белом списке\",\n \"noBlacklistUsers\": \"Нет пользователей в чёрном списке\",\n \"noPendingApprovals\": \"Нет ожидающих одобрения\",\n \"userAdded\": \"Пользователь добавлен\",\n \"userRemoved\": \"Пользователь удалён\",\n \"approveSuccess\": \"Добавлен в белый список\",\n \"denySuccess\": \"Добавлен в чёрный список\",\n \"dismissSuccess\": \"Отклонено\",\n \"operationFailed\": \"Операция не удалась\",\n \"remark\": \"Примечание\",\n \"usernamePlaceholder\": \"Введите имя пользователя (необязательно)\",\n \"remarkPlaceholder\": \"Введите примечание (необязательно)\",\n \"filterByChannel\": \"Фильтр по каналу\",\n \"selectToOperate\": \"Выберите элементы для операции\",\n \"selectedCount\": \"Выбрано: {{count}}\",\n \"batchApprove\": \"Одобрить\",\n \"batchDeny\": \"В чёрный список\",\n \"batchDismiss\": \"Удалить\",\n \"batchApproveConfirm\": \"Одобрить {{count}} пользователей?\",\n \"batchDenyConfirm\": \"Добавить {{count}} пользователей в чёрный список?\",\n \"batchDismissConfirm\": \"Удалить {{count}} пользователей?\",\n \"batchSuccess\": \"{{count}} пользователей обработано\",\n \"batchRemove\": \"Удалить\",\n \"batchRemoveConfirm\": \"Удалить {{count}} выбранных пользователей?\",\n \"enabledSection\": \"Активированные\",\n \"enabledCount\": \"{{count}}\",\n \"availableSection\": \"Доступные\",\n \"enableAction\": \"Активировать\",\n \"noEnabledChannels\": \"Нет активированных каналов\",\n \"goEnableChannels\": \"Активировать каналы\"\n },\n \"sessions\": {\n \"title\": \"Сессии\",\n \"description\": \"Просмотр и управление активными чат-сессиями\",\n \"loading\": \"Загрузка сессий...\",\n \"confirmDelete\": \"Подтвердить удаление\",\n \"deleteConfirm\": \"Вы уверены, что хотите удалить эту сессию?\",\n \"batchDeleteConfirm\": \"Вы уверены, что хотите удалить выбранные сессии ({{count}})?\",\n \"deleteSuccess\": \"Сессия успешно удалена\",\n \"deleteFailed\": \"Не удалось удалить сессию\",\n \"batchDeleteSuccess\": \"Успешно удалено сессий: {{count}}\",\n \"batchDeleteFailed\": \"Не удалось массово удалить сессии\",\n \"saveSuccess\": \"Сессия успешно сохранена\",\n \"saveFailed\": \"Не удалось сохранить сессию\",\n \"batchDeleteButton\": \"Массовое удаление\",\n \"filterTitle\": \"Фильтр по названию\",\n \"filterUserId\": \"Фильтр по ID пользователя\",\n \"filterChannel\": \"Фильтр по каналу\",\n \"allChannels\": \"Все каналы\",\n \"totalItems\": \"Всего {{count}} элементов\",\n \"selectedItems\": \"Выбрано {{count}} элементов\",\n \"editSession\": \"Редактировать сессию\",\n \"pleaseInputName\": \"Пожалуйста, введите название сессии\",\n \"sessionNamePlaceholder\": \"Название сессии\",\n \"activeTab\": \"Активные\",\n \"archivedTab\": \"Архив\",\n \"archivedAtColumn\": \"Дата архивации\",\n \"archive\": {\n \"action\": \"Архивировать\",\n \"unaction\": \"Разархивировать\",\n \"batchAction\": \"Архивировать выбранные\",\n \"batchUnaction\": \"Разархивировать выбранные\",\n \"success\": \"Чат архивирован\",\n \"successHint\": \"Чат архивирован. Смотрите в Панель управления > Сессии > Архив.\",\n \"failed\": \"Не удалось архивировать\",\n \"unarchiveSuccess\": \"Чат разархивирован\",\n \"unarchiveFailed\": \"Не удалось разархивировать\",\n \"batchSuccess\": \"{{count}} чат(ов) архивировано\",\n \"batchFailed\": \"Не удалось архивировать выбранные\",\n \"batchUnarchiveSuccess\": \"{{count}} чат(ов) разархивировано\",\n \"batchUnarchiveFailed\": \"Не удалось разархивировать выбранные\",\n \"groupLabel\": \"Архив ({{count}})\",\n \"modalTitle\": \"Архивированные чаты\",\n \"unarchiveAll\": \"Разархивировать все\",\n \"noArchived\": \"Нет архивированных чатов\"\n }\n },\n \"environments\": {\n \"parent\": \"Настройки\",\n \"environments\": \"Переменные окружения\",\n \"title\": \"Переменные окружения\",\n \"description\": \"Настройка переменных окружения (ключ-значение) для агентов и навыков.\",\n \"key\": \"Ключ\",\n \"value\": \"Значение\",\n \"variableNamePlaceholder\": \"VARIABLE_NAME\",\n \"valuePlaceholder\": \"value\",\n \"insertRowBelow\": \"Вставить строку ниже\",\n \"deleteRow\": \"Удалить строку\",\n \"deleteVariable\": \"Удалить переменную\",\n \"deleteConfirm\": \"Удалить «{{name}}»?\",\n \"deleteSelected\": \"Удалить выбранные\",\n \"deleteSelectedConfirm\": \"Удалить {{label}}?\",\n \"keyRequired\": \"Ключ обязателен\",\n \"invalidKeyFormat\": \"Неверный формат ключа\",\n \"duplicateKey\": \"Дубликат ключа\",\n \"saveSuccess\": \"Переменные окружения сохранены\",\n \"saveFailed\": \"Не удалось сохранить\",\n \"deleteSuccess\": \"«{{name}}» удалено\",\n \"deleteFailed\": \"Не удалось удалить\",\n \"noVariables\": \"Переменные окружения пока не настроены.\",\n \"addVariable\": \"Добавить переменную\",\n \"loading\": \"Загрузка…\",\n \"retry\": \"Повторить\",\n \"of\": \"из\",\n \"selected\": \"выбрано\",\n \"variable\": \"переменная\",\n \"variables\": \"переменных\",\n \"showValue\": \"Показать значение\",\n \"hideValue\": \"Скрыть значение\"\n },\n \"tokenUsage\": {\n \"title\": \"Использование токенов\",\n \"totalTokens\": \"Всего токенов\",\n \"totalCalls\": \"Всего вызовов\",\n \"promptTokens\": \"Входные токены\",\n \"completionTokens\": \"Выходные токены\",\n \"byModel\": \"По модели\",\n \"byDate\": \"По дате\",\n \"model\": \"Модель\",\n \"date\": \"Дата\",\n \"loadFailed\": \"Не удалось загрузить данные об использовании токенов\",\n \"noData\": \"Нет данных об использовании токенов за выбранный период\",\n \"modelTrend\": \"Тренд использования моделей\",\n \"tokenTypeChart\": \"Тренд типов токенов\",\n \"selectAll\": \"Выбрать все\",\n \"allSelected\": \"Все\",\n \"itemsSelected\": \"Выбрано: {{count}}\",\n \"selectModels\": \"Выберите модели\",\n \"selectTokenTypes\": \"Выберите типы токенов\"\n },\n \"agentStats\": {\n \"title\": \"Статистика агента\",\n \"description\": \"Просмотр статистики агента за выбранный период.\",\n \"totalSessions\": \"Всего сессий\",\n \"totalMessages\": \"Всего сообщений\",\n \"userMessages\": \"Сообщения пользователя\",\n \"assistantMessages\": \"Сообщения ассистента\",\n \"promptTokens\": \"Входные токены\",\n \"completionTokens\": \"Выходные токены\",\n \"llmCalls\": \"Вызовы LLM\",\n \"toolCalls\": \"Вызовы инструментов\",\n \"channel\": \"Канал\",\n \"sessionCount\": \"Сессий\",\n \"sessions\": \"Сессии\",\n \"newSessions\": \"Новые сессии\",\n \"activeSessions\": \"Активные сессии\",\n \"messages\": \"Сообщения\",\n \"messageTrend\": \"Динамика сообщений\",\n \"sessionTrend\": \"Динамика сессий\",\n \"tokenTrend\": \"Динамика токенов\",\n \"llmAndToolTrend\": \"Динамика вызовов LLM и инструментов\",\n \"sessionsByChannel\": \"Сессии по каналам\",\n \"messagesByChannel\": \"Сообщения по каналам\",\n \"totalSessionsTooltip\": \"Всего созданных чат-сессий. Каждая сессия — независимый контекст разговора\",\n \"totalMessagesTooltip\": \"Всего сообщений во всех сессиях, включая сообщения пользователя и ассистента\",\n \"promptTokensTooltip\": \"Всего отправленных LLM промпт-токенов\",\n \"completionTokensTooltip\": \"Всего сгенерированных LLM токенов завершения\",\n \"llmCallsTooltip\": \"Количество вызовов LLM API. Каждый запрос считается за один вызов\",\n \"toolCallsTooltip\": \"Количество решений агента вызвать инструмент. Один вызов LLM может породить несколько намерений использования инструментов\",\n \"messageTrendTooltip\": \"Сравнение сообщений пользователя и ассистента по дням\",\n \"sessionTrendTooltip\": \"Сравнение новых и активных сессий по дням. Оранжевые столбцы — созданные в этот день сессии, синие — сессии с сообщениями в этот день\",\n \"tokenTrendTooltip\": \"Динамика потребления входных и выходных токенов\",\n \"llmAndToolTrendTooltip\": \"Сравнение вызовов LLM и намерений использования инструментов по дням\",\n \"sessionsByChannelTooltip\": \"Распределение сессий по каналам. Сессии без канала по умолчанию группируются в Console\",\n \"messagesByChannelTooltip\": \"Распределение общего количества сообщений по каналам\",\n \"loadFailed\": \"Не удалось загрузить статистику\",\n \"noData\": \"Нет статистических данных за выбранный период\",\n \"retry\": \"Повторить\"\n },\n \"modelSelector\": {\n \"selectModel\": \"Выбрать модель\",\n \"noConfiguredModels\": \"Нет настроенных моделей\",\n \"noFreeModels\": \"Нет доступных бесплатных моделей\",\n \"switchFailed\": \"Не удалось переключить модель\",\n \"searchModels\": \"Поиск моделей...\",\n \"noModelsFound\": \"Подходящие модели не найдены\",\n \"free\": \"Бесплатно\",\n \"vision\": \"Видение\",\n \"freeBannerText\": \"Бесплатные модели могут иметь лимиты использования и работать нестабильно. Подробности уточняйте у провайдера.\",\n \"moreProviders\": \"Больше бесплатных провайдеров (нужен API Key)\",\n \"proBannerText\": \"Показаны модели, настроенные в разделе Настройки.\",\n \"connectToUse\": \"Подключить {{provider}} для бесплатных моделей\",\n \"configureApiKey\": \"Настроить API Key {{provider}} для бесплатных моделей\",\n \"configureApiKeyTitle\": \"Перейти к настройке\",\n \"configureApiKeyConfirm\": \"Вы будете перенаправлены на страницу моделей для настройки API Key {{provider}}. Продолжить?\",\n \"goToConfigure\": \"Перейти\",\n \"noModelsDiscovered\": \"Подключено. Загрузка моделей...\",\n \"oauthTitle\": \"Подключиться к {{provider}}\",\n \"oauthDescription\": \"Откроется новое окно браузера для авторизации доступа к {{provider}}. API-ключ будет сохранён автоматически.\",\n \"oauthContinue\": \"Продолжить\",\n \"oauthWaiting\": \"Ожидание авторизации...\",\n \"oauthWaitingDescription\": \"Пожалуйста, завершите авторизацию в окне браузера.\",\n \"oauthConnected\": \"Успешно подключено к {{provider}}\",\n \"oauthFailed\": \"Авторизация не удалась, попробуйте снова\",\n \"viewMore\": \"Показать ещё ({{count}})\"\n },\n \"models\": {\n \"llmConfiguration\": \"Конфигурация LLM\",\n \"providersTitle\": \"Провайдеры\",\n \"providersDescription\": \"Настройте API-ключи и endpoints для каждого провайдера.\",\n \"llmTitle\": \"Модели\",\n \"defaultLlm\": \"LLM по умолчанию\",\n \"llmDescription\": \"Здесь задается глобальная модель LLM по умолчанию у авторизованного провайдера. Для конкретного Agent модель можно выбрать отдельно на странице Chat.\",\n \"configureProvider\": \"Настроить {{name}}\",\n \"baseURL\": \"Базовый URL\",\n \"apiKey\": \"API-ключ\",\n \"advancedConfig\": \"Расширенная конфигурация\",\n \"authMode\": \"Режим аутентификации\",\n \"authModeApiKey\": \"API Key (x-api-key)\",\n \"authModeAuthToken\": \"Auth Token (Authorization: Bearer)\",\n \"customHeaders\": \"Пользовательские заголовки\",\n \"customHeadersHint\": \"Добавляются к каждому запросу API, например x-app: cli\",\n \"customHeaderKey\": \"Название заголовка\",\n \"customHeaderValue\": \"Значение заголовка\",\n \"addHeader\": \"+ Добавить заголовок\",\n \"generateConfig\": \"Параметры генерации\",\n \"generateConfigHint\": \"Параметры генерации в формате JSON будут развёрнуты и переданы в запрос на генерацию (`openai.chat.completions` или `anthropic.messages`).\",\n \"generateConfigInvalidJson\": \"Пожалуйста, введите корректный JSON\",\n \"generateConfigMustBeObject\": \"Параметры генерации должны быть JSON-объектом\",\n \"modelAdvancedConfig\": \"Расширенная конфигурация модели\",\n \"modelGenerateConfig\": \"Параметры генерации модели\",\n \"modelGenerateConfigHint\": \"Параметры генерации в формате JSON для этой модели. Они будут развёрнуты и переданы в запрос на генерацию. Имеют приоритет над расширенной конфигурацией провайдера.\",\n \"modelConfigSaved\": \"Конфигурация модели {{name}} сохранена\",\n \"modelConfigSaveFailed\": \"Не удалось сохранить конфигурацию модели\",\n \"modelConfigLabel\": \"Конфигурация модели\",\n \"maxTokensLabel\": \"Макс. токенов\",\n \"maxTokensHint\": \"Максимальное количество выходных токенов на ответ\",\n \"maxInputLengthLabel\": \"Макс. длина контекста\",\n \"maxInputLengthHint\": \"Размер контекстного окна модели, управляет порогом сжатия контекста (≥1000)\",\n \"relayReasoningLabel\": \"Передавать рассуждения\",\n \"relayReasoningHint\": \"Передавать содержимое рассуждений в последующие ходы диалога\",\n \"thinkingModeLabel\": \"Включить мышление\",\n \"thinkingModeHint\": \"Включите для активации мышления/рассуждения. Выключите для отключения мышления.\",\n \"thinkingBudgetLabel\": \"Бюджет мышления\",\n \"thinkingBudgetHint\": \"Используется бюджет мышления по умолчанию\",\n \"switchToManual\": \"Вручную\",\n \"switchToAuto\": \"Авто\",\n \"reasoningEffortLabel\": \"Глубина рассуждения\",\n \"reasoningEffortHint\": \"Управляет глубиной рассуждения (семейство OpenAI)\",\n \"protocol\": \"Протокол\",\n \"protocolHint\": \"Выберите протокол API провайдера для этой конфигурации.\",\n \"selectProtocol\": \"Пожалуйста, выберите протокол\",\n \"protocolOpenAI\": \"OpenAI-совместимый (Chat Completions)\",\n \"protocolOpenAIResponse\": \"OpenAI-совместимый (Response API)\",\n \"protocolAnthropic\": \"Anthropic (Messages API)\",\n \"currentKey\": \"Текущий: {{key}}\",\n \"startsWith\": \"Начинается с «{{prefix}}»\",\n \"optionalSelfHosted\": \"Необязательно для self-hosted сервисов\",\n \"leaveBlankKeep\": \"Оставьте пустым, чтобы сохранить текущий ключ\",\n \"enterApiKey\": \"Введите API-ключ ({{prefix}}...)\",\n \"enterApiKeyOptional\": \"Введите API-ключ (необязательно)\",\n \"openAIEndpoint\": \"OpenAI endpoint, например https://api.openai.com/v1\",\n \"openAICompatibleEndpoint\": \"OpenAI-совместимый endpoint, например https://api.example.com (добавляйте /v1 только если ваш провайдер этого требует)\",\n \"azureEndpointHint\": \"Azure OpenAI endpoint, например https://<resource>.openai.azure.com/openai/v1\",\n \"anthropicEndpointHint\": \"Anthropic endpoint, например https://api.anthropic.com\",\n \"ollamaEndpointHint\": \"Ollama endpoint, например http://localhost:11434\",\n \"lmstudioEndpointHint\": \"LM Studio endpoint, например http://localhost:1234/v1\",\n \"apiEndpointHint\": \"API endpoint, например https://api.example.com\",\n \"pleaseEnterBaseURL\": \"Пожалуйста, введите базовый URL API\",\n \"selectBaseURL\": \"Выберите базовый URL\",\n \"selectBaseURLHint\": \"Выберите региональную конечную точку этого провайдера\",\n \"pleaseEnterValidURL\": \"Пожалуйста, введите корректный URL\",\n \"apiKeyShouldStart\": \"API-ключ должен начинаться с «{{prefix}}»\",\n \"configurationSaved\": \"Конфигурация {{name}} сохранена\",\n \"failedToSaveConfig\": \"Не удалось сохранить конфигурацию\",\n \"revokeAuthorization\": \"Отозвать авторизацию\",\n \"revokeConfirmContent\": \"Вы уверены, что хотите удалить API-ключ для {{name}}? Текущая конфигурация LLM-модели также будет очищена.\",\n \"revokeConfirmSimple\": \"Вы уверены, что хотите удалить API-ключ для {{name}}?\",\n \"authorizationRevoked\": \"Авторизация {{name}} отозвана, LLM-модель очищена\",\n \"authorizationRevokedSimple\": \"Авторизация {{name}} отозвана\",\n \"failedToRevoke\": \"Не удалось отозвать авторизацию\",\n \"active\": \"Активно: {{provider}} / {{model}}\",\n \"provider\": \"Провайдер\",\n \"model\": \"Модель\",\n \"selectProvider\": \"Выберите провайдера (должен быть авторизован)\",\n \"selectModel\": \"Выберите модель\",\n \"llmModelUpdated\": \"LLM-модель обновлена\",\n \"failedToSave\": \"Не удалось сохранить\",\n \"freeModelWarningTitle\": \"Уведомление о бесплатной модели\",\n \"freeModelWarningMessage\": \"Бесплатные модели могут работать нестабильно. Подробности смотрите в условиях провайдера:\",\n \"freeModelWarningDontShowAgain\": \"Больше не показывать\",\n \"saved\": \"Сохранено\",\n \"save\": \"Сохранить\",\n \"cancel\": \"Отмена\",\n \"loading\": \"Загрузка...\",\n \"retry\": \"Повторить\",\n \"notSet\": \"Не задано\",\n \"notRequired\": \"Не требуется\",\n \"available\": \"Доступно\",\n \"unavailable\": \"Недоступно\",\n \"providerAvailable\": \"Готово (есть модели)\",\n \"providerNoModels\": \"Не готово (нет моделей)\",\n \"search\": \"Поиск\",\n \"searchPlaceholder\": \"Поиск провайдеров...\",\n \"searchModelPlaceholder\": \"Поиск моделей...\",\n \"providerNotConfigured\": \"Не готово (не настроено)\",\n \"settings\": \"Настройки\",\n \"actions\": \"Действия\",\n \"custom\": \"Пользовательский\",\n \"builtin\": \"Встроенный\",\n \"freeTierGroup\": \"Бесплатный уровень\",\n \"paidGroup\": \"Платные\",\n \"localCustomGroup\": \"Локальные и пользовательские\",\n \"endpointCount\": \"{{count}} конечных точек\",\n \"connect\": \"OAuth\",\n \"saveApiKey\": \"Сохранить\",\n \"addProvider\": \"Добавить провайдера\",\n \"addProviderTitle\": \"Добавить пользовательского провайдера\",\n \"providerIdLabel\": \"ID провайдера\",\n \"providerIdPlaceholder\": \"например: openai, google, anthropic\",\n \"providerIdHint\": \"Строчные буквы, цифры, дефисы и подчёркивания. Нельзя изменить позже.\",\n \"providerNameLabel\": \"Отображаемое имя\",\n \"providerNamePlaceholder\": \"например: OpenAI, Google Gemini\",\n \"defaultBaseUrlLabel\": \"Базовый URL по умолчанию\",\n \"defaultBaseUrlPlaceholder\": \"например: https://api.example.com\",\n \"apiKeyPrefixLabel\": \"Префикс API-ключа (необязательно)\",\n \"apiKeyPrefixPlaceholder\": \"например: sk-\",\n \"providerCreated\": \"Провайдер «{{name}}» создан\",\n \"providerCreateFailed\": \"Не удалось создать провайдера\",\n \"deleteProvider\": \"Удалить провайдера\",\n \"deleteProviderConfirm\": \"Удалить пользовательского провайдера «{{name}}» и все его модели? Это действие нельзя отменить.\",\n \"providerDeleted\": \"Провайдер «{{name}}» удалён\",\n \"providerDeleteFailed\": \"Не удалось удалить провайдера\",\n \"manageModels\": \"Модели\",\n \"models\": \"Модели\",\n \"manageModelsTitle\": \"{{provider}} — Управление моделями\",\n \"userAdded\": \"Добавлено пользователем\",\n \"addModel\": \"Добавить модель\",\n \"addModels\": \"Добавить модели\",\n \"autoDiscoverModels\": \"Автообнаружение моделей\",\n \"addModelTitle\": \"Добавить модель в {{provider}}\",\n \"modelIdLabel\": \"ID модели\",\n \"modelIdPlaceholder\": \"например: gpt-4o, gemini-2.0-flash\",\n \"modelDiscoveryUnavailableHint\": \"Доступные модели не найдены. Возможно, этот провайдер не поддерживает получение списка моделей, поэтому введите ID модели вручную.\",\n \"modelNameLabel\": \"Название модели\",\n \"modelNameRequired\": \"Пожалуйста, введите название модели\",\n \"modelNamePlaceholder\": \"например: GPT-4o, Gemini 2.0 Flash\",\n \"modelAdded\": \"Модель «{{name}}» добавлена\",\n \"modelAddFailed\": \"Не удалось добавить модель\",\n \"modelAlreadyExists\": \"Модель с ID \\\"{{id}}\\\" уже существует\",\n \"autoDiscoverModelsSuccess\": \"Автообнаружение завершено. Добавлено моделей: {{count}}\",\n \"autoDiscoverModelsNoNew\": \"Автообнаружение завершено. Новых моделей не найдено\",\n \"autoDiscoverModelsFailed\": \"Не удалось автоматически обнаружить модели\",\n \"removeModel\": \"Удалить\",\n \"removeModelConfirm\": \"Удалить модель «{{name}}» из {{provider}}?\",\n \"modelRemoved\": \"Модель «{{name}}» удалена\",\n \"modelRemoveFailed\": \"Не удалось удалить модель\",\n \"loadMore\": \"Загрузить ещё ({{count}})\",\n \"modelsCount\": \"{{count}} моделей\",\n \"noModels\": \"Нет моделей\",\n \"addModelFirst\": \"Сначала добавьте модель\",\n \"modelList\": \"Список моделей\",\n \"clearAll\": \"Очистить всё\",\n \"filteredModelsLoaded\": \"Загружено отфильтрованных моделей: {{count}}\",\n \"filterFailed\": \"Не удалось отфильтровать модели\",\n \"clearAllModels\": \"Очистить все модели\",\n \"clearAllModelsConfirm\": \"Удалить все дополнительные модели (всего {{count}})? Это действие нельзя отменить.\",\n \"allModelsCleared\": \"Число удаленных моделей: {{count}}\",\n \"filterModels\": \"Фильтровать модели\",\n \"filterByProvider\": \"Провайдер:\",\n \"searchProviderPlaceholder\": \"Поиск по названию провайдера\",\n \"selectAllProviders\": \"Выбрать все\",\n \"clearProviderSelection\": \"Снять все\",\n \"noMatchingProviders\": \"Нет подходящих провайдеров\",\n \"filterByModality\": \"Модальность ввода:\",\n \"filterFreeOnly\": \"Только бесплатные модели:\",\n \"getModels\": \"Получить модели\",\n \"discovered\": \"Доступные модели:\",\n \"add\": \"Добавить\",\n \"local\": \"Локальные\",\n \"localType\": \"Тип\",\n \"localEmbedded\": \"Встроенная (в процессе)\",\n \"localDownloadFirst\": \"Сервис модели не запущен\",\n \"localDownloadModel\": \"Скачать модель\",\n \"localModelsTitle\": \"{{provider}} — Локальные модели\",\n \"localNoModels\": \"Пока нет скачанных моделей\",\n \"localRepoId\": \"ID репозитория модели\",\n \"localRepoIdRequired\": \"Пожалуйста, введите ID репозитория модели\",\n \"localRepoIdPlaceholder\": \"например: Qwen/Qwen3-0.6B-GGUF\",\n \"localFilename\": \"Имя файла (необязательно)\",\n \"localFilenamePlaceholder\": \"например: mistral-7b.Q4_K_M.gguf\",\n \"localFilenameHint\": \"Оставьте пустым для автоматического выбора лучшей квантизации\",\n \"localSource\": \"Источник\",\n \"localDownloadSuccess\": \"Модель успешно скачана\",\n \"localDownloadFailed\": \"Не удалось скачать модель\",\n \"localDeleteModel\": \"Удалить модель\",\n \"localDeleteConfirm\": \"Удалить локальную модель «{{name}}»? Файл модели будет удалён с диска.\",\n \"localModelDeleted\": \"Модель «{{name}}» удалена\",\n \"localDeleteFailed\": \"Не удалось удалить модель\",\n \"localDownloadPending\": \"Подготовка к загрузке...\",\n \"localDownloading\": \"Загрузка {{repo}}... Это может занять несколько минут.\",\n \"localDownloadNavigateHint\": \"Вы можете перейти на другую страницу — загрузка продолжится в фоне.\",\n \"localDownloadInProgress\": \"Загрузка уже выполняется\",\n \"localDownloaded\": \"Загружено\",\n \"localNotDownloaded\": \"Не загружено\",\n \"localNoRecommendedModels\": \"Текущая конфигурация устройства слишком слабая для запуска локальных моделей\",\n \"localCancelDownloadTitle\": \"Отменить загрузку\",\n \"localCancelDownloadAction\": \"Отменить загрузку\",\n \"localCancelDownloadConfirm\": \"Отменить загрузку «{{repo}}»?\",\n \"localDownloadCancelled\": \"Загрузка отменена\",\n \"localCancelDownloadFailed\": \"Не удалось отменить загрузку\",\n \"localLlamacppName\": \"Движок инференса\",\n \"localLlamacppInstallHint\": \"Сначала установите локальную среду, затем можно скачивать или запускать модель.\",\n \"localInstallLlamacpp\": \"Установить llama.cpp\",\n \"localLlamacppInstallStarted\": \"Загрузка llama.cpp началась\",\n \"localLlamacppInstallSuccess\": \"llama.cpp успешно установлен\",\n \"localLlamacppInstallFailed\": \"Не удалось установить llama.cpp\",\n \"localRuntimeSectionTitle\": \"Inference Engine\",\n \"localRuntimeSectionDescription\": \"Powered by Llama.cpp\",\n \"localRuntimeComputeHint\": \"По умолчанию используется CPU. Если нужна GPU-акселерация, используйте Ollama или LM Studio.\",\n \"localRuntimeInstalled\": \"Установлен\",\n \"localRuntimeUpdateAvailable\": \"Доступно обновление\",\n \"localRuntimeUpdateAction\": \"Нажмите, чтобы скачать последнюю версию\",\n \"localRuntimeUpdateConfirmTitle\": \"Подтвердите обновление llama.cpp\",\n \"localRuntimeUpdateConfirmContent\": \"Обновление перезапишет текущую установленную версию llama.cpp. Подтвердите, чтобы начать загрузку и установку последней версии.\",\n \"localRuntimeUpdateConfirmContentWithServer\": \"Обновление перезапишет текущую установленную версию llama.cpp и остановит запущенный сервис модели ({{model}}). Подтвердите, чтобы начать загрузку и установку последней версии.\",\n \"localRuntimeUnsupported\": \"Не поддерживается\",\n \"localRuntimeMissing\": \"Не установлен\",\n \"localServerRunningTitle\": \"Текущий локальный сервер\",\n \"localServerOnline\": \"Работает\",\n \"localServerIdle\": \"Недоступен\",\n \"localServerUnavailable\": \"Ошибка\",\n \"localNoActiveModel\": \"Нет активной модели\",\n \"localNoDownloadedModelsHint\": \"Сначала скачайте подходящую модель, а затем запускайте локальный сервис инференса.\",\n \"localDownloadModelFirst\": \"Сначала скачайте модель\",\n \"localEngineInstallStateLabel\": \"Установка\",\n \"localEngineRunStateLabel\": \"Статус\",\n \"localEngineCurrentModelLabel\": \"Текущая обслуживаемая модель\",\n \"localEngineNextStepLabel\": \"Следующий шаг\",\n \"localEngineHealthLabel\": \"Состояние движка\",\n \"localEngineStatusHint\": \"Скачайте и запустите подходящую модель из списка ниже\",\n \"localStartServer\": \"Запустить\",\n \"localStopServer\": \"Остановить\",\n \"localServerStartFailed\": \"Не удалось запустить локальный сервер\",\n \"localServerStopFailed\": \"Не удалось остановить локальный сервер\",\n \"localServerSwitchTitle\": \"Переключить запущенную модель\",\n \"localServerSwitchConfirm\": \"Сейчас запущена модель {{current}}. Переключить на {{next}}?\",\n \"localSwitchModel\": \"Переключить\",\n \"localModelPathMissing\": \"Для этой загруженной модели отсутствует локальный путь.\",\n \"localModelsSectionTitle\": \"Локальные модели\",\n \"localModelsSectionDescription\": \"Загруженные модели можно запускать сразу. Одновременно может выполняться только одна загрузка модели и один локальный сервер.\",\n \"localModelsLockedHint\": \"Сначала установите llama.cpp\",\n \"localAlternativeRuntimeHint\": \"Попробуйте использовать Ollama или LM Studio\",\n \"localModelsCount\": \"Загружено {{downloaded}} / {{total}}\",\n \"localCurrentDownloadTitle\": \"Текущая загрузка\",\n \"localAdvancedConfigTitle\": \"Расширенные настройки локальной модели\",\n \"localAdvancedConfigDescription\": \"Эти настройки применяются к встроенному локальному рантайму инференса. Изменения максимальной длины контекста вступят в силу при следующем запуске локального сервера.\",\n \"localAdvancedConfigSaved\": \"Расширенные настройки локальной модели сохранены\",\n \"localAdvancedConfigSaveFailed\": \"Не удалось сохранить расширенные настройки локальной модели\",\n \"localMaxContextLengthLabel\": \"Макс. длина контекста\",\n \"localMaxContextLengthHint\": \"Максимальное число токенов в контекстном окне модели, определяющее, сколько истории помещается в каждый запрос. Изменения вступят в силу при следующем запуске локального сервера.\",\n \"localMaxContextLengthRequired\": \"Укажите макс. длину контекста\",\n \"localMaxContextLengthMin\": \"Макс. длина контекста должна быть не меньше {{min}}\",\n \"localMaxContextLengthPlaceholder\": \"Введите макс. длину контекста\",\n \"localServerPortLabel\": \"Порт сервера\",\n \"localServerPortHint\": \"Оставьте поле пустым, чтобы llama.cpp автоматически выбрал свободный порт при запуске. Укажите порт, если хотите использовать фиксированный порт при следующих запусках. Изменения вступят в силу при следующем запуске локального сервера.\",\n \"localServerPortRange\": \"Порт сервера должен быть в диапазоне от {{min}} до {{max}}\",\n \"localServerPortPlaceholder\": \"Оставьте пустым для автовыбора порта\",\n \"localGenerateConfigHint\": \"Настройте параметры генерации локальной модели в виде JSON-объекта. Они будут развёрнуты в последующие запросы генерации.\",\n \"localCustomModelTitle\": \"Загрузка пользовательской модели\",\n \"localCustomModelHint\": \"Введите ID репозитория модели. Поддерживается только GGUF.\",\n \"localSourceHuggingFace\": \"Hugging Face\",\n \"localSourceModelScope\": \"ModelScope\",\n \"ollamaModelNamePlaceholder\": \"например: mistral:7b, qwen3:8b\",\n \"testConnection\": \"Проверить подключение\",\n \"testConnectionSuccess\": \"Проверка подключения прошла успешно\",\n \"testConnectionFailed\": \"Проверка подключения не удалась\",\n \"testConnectionFailedWithMessage\": \"Проверка подключения не удалась: {{message}}\",\n \"testConnectionError\": \"Произошла ошибка при проверке подключения\",\n \"discoverModels\": \"Обнаружить модели\",\n \"discoverModelsFailed\": \"Не удалось обнаружить модели\",\n \"ollamaFetchModelsFailed\": \"Не удалось получить список моделей Ollama. Проверьте подробности ошибки.\",\n \"modelTestFailed\": \"Проверка модели не пройдена, проверьте корректность ID модели\",\n \"modelTestFailedConfirm\": \"Тест подключения модели не пройден: {{message}}. Всё равно добавить эту модель?\",\n \"autoDiscoveredAndAdded\": \"Автоматически обнаружено {{count}} моделей и добавлено {{added}} новых\",\n \"autoDiscoveredNoNew\": \"Автоматически обнаружено {{count}} моделей; список моделей уже актуален\",\n \"autoDiscoverFailed\": \"Автоматическое обнаружение моделей не удалось; вы можете добавить модели вручную\",\n \"tagImage\": \"Изображение\",\n \"tagVideo\": \"Видео\",\n \"tagTextOnly\": \"Только текст\",\n \"tagMultimodal\": \"Мультимодальность\",\n \"tagVision\": \"Зрение\",\n \"tagText\": \"Текст\",\n \"free\": \"Бесплатно\",\n \"tagNotProbed\": \"Не проверено\",\n \"probeMultimodal\": \"Тест мультимодальности\",\n \"probeImage\": \"Изображение\",\n \"probeVideo\": \"Видео\",\n \"probeSupported\": \"Поддерживается: {{types}}\",\n \"probeNotSupported\": \"Эта модель не поддерживает мультимодальный ввод\",\n \"probeFailed\": \"Ошибка проверки\",\n \"modalityVision\": \"Изображение\",\n \"modalityAudio\": \"Аудио\",\n \"modalityVideo\": \"Видео\",\n \"modalityFile\": \"Файл\",\n \"modalityText\": \"Только текст\",\n \"perMillionIn\": \"/1M вх.\",\n \"perMillionOut\": \"/1M исх.\"\n },\n \"agentConfig\": {\n \"title\": \"Конфигурация\",\n \"description\": \"Настройка параметров выполнения агента\",\n \"reactAgentTitle\": \"Агент ReAct\",\n \"llmRetryTitle\": \"Автоповтор LLM\",\n \"llmRetryEnabled\": \"Включить автоповтор\",\n \"llmRetryEnabledTooltip\": \"Автоматически повторять временные ошибки LLM API, такие как лимиты, таймауты и проблемы соединения. Применяется к новым запросам после сохранения.\",\n \"llmMaxRetries\": \"Макс. число повторов\",\n \"llmMaxRetriesTooltip\": \"Максимальное количество дополнительных повторов при временной ошибке LLM.\",\n \"llmMaxRetriesPlaceholder\": \"Введите макс. число повторов\",\n \"llmMaxRetriesRequired\": \"Требуется указать макс. число повторов\",\n \"llmMaxRetriesMin\": \"Макс. число повторов должно быть не меньше 1\",\n \"llmBackoffBase\": \"Базовая задержка backoff (с)\",\n \"llmBackoffBaseTooltip\": \"Базовая задержка перед первой повторной попыткой. Дальше используется экспоненциальный backoff.\",\n \"llmBackoffBasePlaceholder\": \"Введите базовую задержку\",\n \"llmBackoffBaseRequired\": \"Требуется указать базовую задержку backoff\",\n \"llmBackoffBaseMin\": \"Базовая задержка backoff должна быть не меньше 0.1 секунды\",\n \"llmBackoffCap\": \"Макс. задержка backoff (с)\",\n \"llmBackoffCapTooltip\": \"Максимальный предел задержки для экспоненциального backoff.\",\n \"llmBackoffCapPlaceholder\": \"Введите макс. задержку\",\n \"llmBackoffCapRequired\": \"Требуется указать макс. задержку backoff\",\n \"llmBackoffCapMin\": \"Макс. задержка backoff должна быть не меньше 0.5 секунды\",\n \"llmBackoffCapGteBase\": \"Макс. задержка backoff должна быть не меньше базовой задержки\",\n \"llmRateLimiterTitle\": \"Ограничитель скорости LLM\",\n \"llmMaxConcurrent\": \"Макс. одновременных запросов\",\n \"llmMaxConcurrentTooltip\": \"Максимальное число одновременно выполняемых LLM-запросов. Общее для всех агентов; вступает в силу при первой инициализации и после перезапуска.\",\n \"llmMaxConcurrentPlaceholder\": \"Введите макс. число одновременных запросов\",\n \"llmMaxConcurrentRequired\": \"Требуется указать макс. число одновременных запросов\",\n \"llmMaxConcurrentRange\": \"Макс. число одновременных запросов должно быть не менее 1\",\n \"llmMaxQpm\": \"Макс. запросов в минуту (QPM)\",\n \"llmMaxQpmTooltip\": \"Максимальное число запросов в 60-секундном скользящем окне. Запросы, превышающие лимит, ожидают перед отправкой — это предотвращает ошибки 429. 0 = без ограничений.\",\n \"llmMaxQpmPlaceholder\": \"Введите макс. QPM (0 = без ограничений)\",\n \"llmMaxQpmRequired\": \"Требуется указать макс. QPM\",\n \"llmMaxQpmRange\": \"Макс. QPM должен быть не менее 0\",\n \"llmRateLimitPause\": \"Пауза при лимите (с)\",\n \"llmRateLimitPauseTooltip\": \"Длительность паузы (в секундах) при получении ответа 429. Переопределяется заголовком Retry-After от API.\",\n \"llmRateLimitPausePlaceholder\": \"Введите длительность паузы\",\n \"llmRateLimitPauseRequired\": \"Требуется указать паузу при лимите\",\n \"llmRateLimitPauseMin\": \"Пауза при лимите должна быть не менее 1 секунды\",\n \"llmRateLimitJitter\": \"Диапазон джиттера (с)\",\n \"llmRateLimitJitterTooltip\": \"Случайный диапазон джиттера (в секундах), добавляемый к паузе для разбивки одновременных пробуждений.\",\n \"llmRateLimitJitterPlaceholder\": \"Введите диапазон джиттера\",\n \"llmRateLimitJitterRequired\": \"Требуется указать диапазон джиттера\",\n \"llmRateLimitJitterMin\": \"Диапазон джиттера должен быть не менее 0 секунд\",\n \"llmAcquireTimeout\": \"Таймаут получения слота (с)\",\n \"llmAcquireTimeoutTooltip\": \"Максимальное время ожидания слота ограничителя скорости перед ошибкой.\",\n \"llmAcquireTimeoutPlaceholder\": \"Введите таймаут получения слота\",\n \"llmAcquireTimeoutRequired\": \"Требуется указать таймаут получения слота\",\n \"llmAcquireTimeoutMin\": \"Таймаут получения слота должен быть не менее 10 секунд\",\n \"llmAcquireTimeoutGtPauseJitter\": \"Таймаут получения слота должен быть больше суммы паузы и джиттера\",\n \"contextManagementTitle\": \"Управление контекстом\",\n \"memoryManagerBackend\": \"Бэкенд управления долговременной памятью\",\n \"memoryManagerBackendTooltip\": \"Тип бэкенда менеджера памяти. Выберите remelight (локальные файлы) или adbpg (облачная база данных).\",\n \"memoryManagerBackendRestartWarning\": \"Смена бэкенда управления долговременной памятью не поддерживает горячую перезагрузку. Сохраните и перезапустите QwenPaw для применения изменений.\",\n \"maxIters\": \"Макс. итераций\",\n \"maxItersTooltip\": \"Максимальное число итераций рассуждение-действие для агента ReAct\",\n \"maxItersPlaceholder\": \"Введите макс. число итераций\",\n \"maxItersRequired\": \"Требуется указать макс. число итераций\",\n \"maxItersMin\": \"Макс. число итераций должно быть не меньше 1\",\n \"agentLoopTitle\": \"Настройки Loop агента\",\n \"loopMode\": {\n \"builtIn\": \"Встроенный\",\n \"builtInNote\": \"Порядок заблокирован · Значения доступны\",\n \"templateSectionTitle\": \"Шаблоны Loop\",\n \"templateSectionDescription\": \"Выберите встроенный шаблон или добавьте свой.\",\n \"defaultTab\": \"По умолчанию\",\n \"goalTab\": \"Цель\",\n \"missionTab\": \"Миссия\",\n \"defaultDescription\": \"Стандартный защищённый ReAct Loop вне явного режима.\",\n \"goalDescription\": \"Ограниченный постоянный Loop для проверяемых целей.\",\n \"missionDescription\": \"Постоянный процесс для длительных многоэтапных миссий.\",\n \"gatePipeline\": \"Цепочка Gate\",\n \"repetitionTitle\": \"Защита от повторов\",\n \"repetitionDescription\": \"Обнаруживает повторные вызовы инструментов и вмешивается.\",\n \"iterationTitle\": \"Лимит итераций\",\n \"iterationDescription\": \"Ограничивает число итераций ReAct.\",\n \"retryTitle\": \"Качественная проверка завершения\",\n \"retryDescription\": \"Срабатывает только для текстового ответа без вызовов инструментов.\",\n \"goalTurnTitle\": \"Лимит ходов цели\",\n \"goalTurnDescription\": \"Ограничивает ходы внутри активной цели.\",\n \"maxGoalTurns\": \"Максимум ходов цели\",\n \"goalBudgetTitle\": \"Бюджет токенов цели\",\n \"goalBudgetDescription\": \"Останавливает цель при достижении бюджета.\",\n \"maxGoalTokens\": \"Максимум токенов цели\",\n \"goalCompletionTitle\": \"Проверка завершения цели\",\n \"goalCompletionDescription\": \"Читает явный статус цели перед остановкой.\",\n \"goalCompletionReadOnly\": \"Завершение контролируется встроенным протоколом цели.\",\n \"missionProgressTitle\": \"Проверка прогресса миссии\",\n \"missionProgressDescription\": \"Продолжает до успеха всех историй или лимита.\",\n \"missionProgressReadOnly\": \"Завершение определяется встроенным статусом Mission Story.\",\n \"missionIterationTitle\": \"Лимит итераций Mission\",\n \"missionIterationDescription\": \"Задайте стандартный лимит выполнения для новых Mission.\",\n \"workerAttemptsTitle\": \"Попытки Worker\",\n \"workerAttemptsDescription\": \"Ограничьте повторы Worker после неудачной проверки Story.\",\n \"missionVerificationTitle\": \"Политика проверки Mission\",\n \"missionVerificationDescription\": \"Задайте стандартные инструкции проверки и тестовую команду.\",\n \"defaultMaxMissionIterations\": \"Максимум раундов выполнения по умолчанию\",\n \"defaultMaxMissionIterationsTooltip\": \"Используется, если /mission не задаёт --max-iterations.\",\n \"maxRetriesPerStory\": \"Максимум повторов для Story\",\n \"maxRetriesPerStoryTooltip\": \"Повторы Worker после результата FAIL или PARTIAL от Verifier.\",\n \"defaultVerificationInstructions\": \"Инструкции по проверке (необязательно)\",\n \"defaultVerificationInstructionsHelp\": \"Инструкции на естественном языке для каждого Mission Verifier.\",\n \"defaultVerificationInstructionsPlaceholder\": \"Например: проверьте пути Windows и фактический интерфейс.\",\n \"defaultVerifyCommand\": \"Тестовая команда по умолчанию (необязательно)\",\n \"defaultVerifyCommandHelp\": \"Verifier запускает её обычными инструментами и с обычными разрешениями. /mission --verify переопределяет значение.\",\n \"defaultVerifyCommandPlaceholder\": \"Например: pytest -q\",\n \"enableRepetitionProtection\": \"Включить защиту от повторов\",\n \"removeRule\": \"Удалить правило\",\n \"custom\": \"Пользовательский\",\n \"customDescription\": \"Создайте сохраняемую цепочку из встроенных Gate QwenPaw.\",\n \"duplicate\": \"Дублировать\",\n \"delete\": \"Удалить\",\n \"displayName\": \"Отображаемое имя\",\n \"duplicateModeName\": \"Режим Loop с таким именем уже существует.\",\n \"slashCommand\": \"Slash-команда\",\n \"description\": \"Описание\",\n \"availableToAgent\": \"Доступен этому агенту\",\n \"runsTopToBottom\": \"Выполняется сверху вниз\",\n \"addGate\": \"Добавить Gate\",\n \"emptyPipeline\": \"Добавьте хотя бы один Gate, чтобы режим стал доступен.\",\n \"iterationGateTitle\": \"Лимит итераций\",\n \"iterationGateDescription\": \"Остановка после заданного числа итераций Loop.\",\n \"doomGateTitle\": \"Защита от повторов\",\n \"doomGateDescription\": \"Обнаруживает повторные вызовы и меняет стратегию.\",\n \"tokenGateTitle\": \"Бюджет токенов\",\n \"tokenGateDescription\": \"Ограничивает токены запроса и ответа.\",\n \"timeoutGateTitle\": \"Лимит времени Loop\",\n \"timeoutGateDescription\": \"Остановка на следующей границе Loop без прерывания текущей операции.\",\n \"toolBudgetGateTitle\": \"Бюджет вызовов инструментов\",\n \"toolBudgetGateDescription\": \"Ограничивает все и выбранные вызовы.\",\n \"retryGateTitle\": \"Проверка ранней остановки\",\n \"retryGateDescription\": \"Просит агента проверить результат перед завершением.\",\n \"rubricGateTitle\": \"Критерии завершения\",\n \"rubricGateDescription\": \"Оценивает явные критерии и запускает исправление.\",\n \"qualitativeRubricTitle\": \"Качественная проверка завершения\",\n \"qualitativeRubricDescription\": \"Проверяет текстовые ответы без вызовов инструментов по критериям на естественном языке.\",\n \"qualitativeRubric\": \"Критерии завершения на естественном языке\",\n \"qualitativeRubricHelp\": \"Срабатывает только для текстовых ответов без вызовов инструментов и продолжает работу, если она не завершена.\",\n \"completionRubricTitle\": \"Проверка сигнала завершения\",\n \"completionRubricDescription\": \"Проверяет сигнал завершения в текстовом ответе без вызовов инструментов.\",\n \"completionRubricHelp\": \"Срабатывает после текстовых ответов без вызовов инструментов. По завершении агент выводит сигнал; иначе продолжает работу до достижения максимального числа проверок.\",\n \"completionPrompt\": \"Промпт проверки завершения\",\n \"completionPromptHelp\": \"Опишите, когда результат считается завершённым. QwenPaw автоматически добавит инструкцию вывода сигнала завершения.\",\n \"completionSignal\": \"Сигнал завершения\",\n \"completionSignalHelp\": \"QwenPaw добавляет этот сигнал и требование вывести только его в промпт проверки. Loop завершается только при полном совпадении без учёта регистра и крайних пробелов.\",\n \"removeCriterion\": \"Удалить критерий {{number}}\",\n \"totalTokensHelp\": \"Максимум входных и выходных токенов вместе за этот ход.\",\n \"promptTokensHelp\": \"Включает системные инструкции, контекст, схемы инструментов и их результаты.\",\n \"completionTokensHelp\": \"Максимум токенов, создаваемых моделью за этот ход.\",\n \"maxIterations\": \"Максимум итераций\",\n \"historyWindow\": \"Окно истории\",\n \"similarityThreshold\": \"Порог сходства\",\n \"totalTokens\": \"Максимум всех токенов\",\n \"promptTokens\": \"Максимум входных токенов\",\n \"completionTokens\": \"Максимум выходных токенов\",\n \"maxSeconds\": \"Максимум секунд\",\n \"maxSecondsHelp\": \"Время проверяется после завершения текущего ответа модели или вызова инструмента. Выполняемая операция не прерывается.\",\n \"allToolCalls\": \"Все вызовы инструментов\",\n \"perToolLimits\": \"Лимиты по инструментам\",\n \"toolName\": \"Имя инструмента\",\n \"callLimitAria\": \"Лимит вызовов {{name}}\",\n \"removeToolLimitAria\": \"Удалить лимит {{name}}\",\n \"addToolLimit\": \"Добавить лимит инструмента\",\n \"maxRetries\": \"Максимум повторов\",\n \"retryInstruction\": \"Инструкция повтора\",\n \"passThreshold\": \"Порог прохождения\",\n \"maxEvaluations\": \"Максимум раундов оценки\",\n \"graderError\": \"Ошибка оценщика\",\n \"stopSafely\": \"Безопасно остановить\",\n \"verifyOnceMore\": \"Проверить ещё раз\",\n \"completionCriteria\": \"Критерии завершения\",\n \"criterionPlaceholder\": \"Опишите наблюдаемый результат\",\n \"required\": \"Обязательно\",\n \"weight\": \"Вес\",\n \"addCriterion\": \"Добавить критерий\",\n \"moveGateAria\": \"Переместить {{title}}\",\n \"removeGate\": \"Удалить Gate\",\n \"newModeName\": \"Новый режим Loop\",\n \"defaultCustomDescription\": \"Пользовательская цепочка Gate.\",\n \"copyName\": \"{{name}} — копия\",\n \"createModeAria\": \"Создать режим Loop\",\n \"createModeTitle\": \"Создать пользовательский режим Loop\",\n \"startingTemplate\": \"Начальный шаблон\",\n \"startingPipeline\": \"Начальный конвейер\",\n \"blankPipelineHint\": \"Начните с пустого режима и добавьте гейты позже\",\n \"safeTemplate\": \"Безопасный запуск\",\n \"researchTemplate\": \"Исследование с бюджетом\",\n \"qualityTemplate\": \"Приоритет качества\",\n \"blankTemplate\": \"Пустая цепочка\"\n },\n \"doomLoopEnabled\": \"Защита от повторений\",\n \"doomLoopEnabledTooltip\": \"Автоматическое вмешательство, когда агент застревает на повторении одних и тех же действий\",\n \"doomLoopAfter\": \"После\",\n \"doomLoopRepetitions\": \"одинаковых действий\",\n \"doomLoopStopAction\": \"Пауза и запрос помощи\",\n \"doomLoopWarnAction\": \"Отправить напоминание\",\n \"doomLoopSimpleMode\": \"Простой\",\n \"doomLoopAdvancedMode\": \"Расширенный\",\n \"doomLoopWindowSize\": \"Диапазон обнаружения\",\n \"doomLoopWindowSizeTooltip\": \"Сколько последних действий проверять на повторение\",\n \"doomLoopSimilarity\": \"Чувствительность совпадения\",\n \"doomLoopSimilarityTooltip\": \"Насколько похожими должны быть действия для обнаружения повтора (ниже = строже)\",\n \"doomLoopStages\": \"Правила вмешательства\",\n \"doomLoopAction\": \"Действие\",\n \"doomLoopPrompt\": \"Сообщение\",\n \"doomLoopPromptPlaceholder\": \"Текст напоминания или причина паузы...\",\n \"doomLoopAddStage\": \"Добавить правило\",\n \"iterationTitle\": \"Лимит итераций\",\n \"iterationEnabled\": \"Включить лимит итераций\",\n \"iterationEnabledTooltip\": \"Остановить агента после фиксированного числа циклов\",\n \"iterationMaxIterations\": \"Максимум итераций\",\n \"iterationMaxIterationsTooltip\": \"Максимальное число циклов до остановки\",\n \"rubricTitle\": \"Качественная проверка завершения\",\n \"rubricEnabled\": \"Включить качественную проверку завершения\",\n \"rubricEnabledTooltip\": \"Срабатывает только для текстового ответа без вызовов инструментов.\",\n \"rubricPrompt\": \"Критерии завершения на естественном языке\",\n \"rubricPromptTooltip\": \"Промпт для запроса оценки завершённости задачи\",\n \"rubricDesc\": \"Для текстового ответа без вызовов инструментов применяет критерии завершения на естественном языке и просит продолжить, если работа осталась.\",\n \"rubricPromptPlaceholder\": \"Вы не вызвали ни одного инструмента. Если задача завершена, подтвердите. В противном случае продолжайте с вызовами инструментов.\",\n \"rubricMaxInterventions\": \"Максимум раундов оценки\",\n \"rubricMaxInterventionsTooltip\": \"Максимальное число проверок завершения текстовых ответов без вызовов инструментов за один ход.\",\n \"inLoopModes\": \"Включить в режимах /goal и /mission\",\n \"inLoopModesTooltip\": \"Также применять, когда активен режим /goal или /mission\",\n \"comingSoonEditable\": \"Пользовательская настройка будет доступна в будущем выпуске.\",\n \"simpleMode\": \"Простой\",\n \"advancedMode\": \"Расширенный\",\n \"comingSoon\": \"Скоро\",\n \"reactModeTab\": \"Шаблон Loop - По умолчанию\",\n \"goalModeTab\": \"Шаблон Loop - Goal\",\n \"missionModeTab\": \"Шаблон Loop - Mission\",\n \"goalModeInfoTitle\": \"Режим Goal vs По умолчанию\",\n \"goalModeInfo\": \"Режим по умолчанию останавливается после ответа. Режим Goal заставляет агента циклически работать над целью, используя Rubric-оценку для определения завершения. Все операции выполняются в текущем контексте агента.\",\n \"missionModeInfoTitle\": \"Режим Mission vs Goal\",\n \"missionModeInfo\": \"Режим Mission автоматически разбивает сложные задачи на подзадачи, выполняемые независимыми Worker-агентами и проверяемые Verifier-агентами. Каждая подзадача выполняется в собственном контексте, не загрязняя историю основной сессии.\",\n \"goalIterationGate\": \"Шлюз итераций цели\",\n \"goalIterationGateDesc\": \"Ограничивает количество ходов агента в сессии цели, отслеживая итерации и использование токенов.\",\n \"goalBudgetGate\": \"Шлюз бюджета токенов\",\n \"goalBudgetGateDesc\": \"Устанавливает лимит расхода токенов для сессии цели. Останавливает агента при превышении бюджета.\",\n \"goalRubricGate\": \"Рубрика завершения цели\",\n \"goalRubricGateDesc\": \"Оценивает, достигнута ли цель, проверяя статус сессии.\",\n \"goalDoomGate\": \"Защита от повторений\",\n \"goalDoomGateDesc\": \"Обнаруживает повторяющиеся паттерны во время выполнения цели и инициирует вмешательство.\",\n \"missionProgressGate\": \"Шлюз прогресса миссии\",\n \"missionProgressGateDesc\": \"Отслеживает завершение пользовательских историй PRD. Продолжает до прохождения всех историй или достижения максимума итераций.\",\n \"missionIterBypass\": \"Обход итераций\",\n \"missionIterBypassDesc\": \"Временно снимает ограничение итераций ReAct во время выполнения миссии для длительных фаз.\",\n \"customLoopTitle\": \"Пользовательские режимы цикла\",\n \"customLoopDesc\": \"Создавайте свои режимы цикла с пользовательскими комбинациями шлюзов. Скоро.\",\n \"autoContinueOnTextOnly\": \"Автопродолжение при только тексте\",\n \"autoContinueOnTextOnlyTooltip\": \"Если шаг рассуждения завершился только текстом без вызова инструментов, вставляется подсказка и выполняется ещё один проход с той же политикой инструментов, чтобы реже обрывались многошаговые задачи. Выключите для прежнего поведения. При включении возможен рост расхода токенов.\",\n \"autoGenerateSessionTitle\": \"Автогенерация заголовков сессий\",\n \"autoGenerateSessionTitleTooltip\": \"После первого пользовательского сообщения в новом чате фоновая задача делает короткий LLM-вызов и заменяет усечённую заглушку на лаконичный заголовок. На каждый новый чат приходится один дополнительный короткий LLM-вызов. Отключите, чтобы оставить заглушку и не тратить токены.\",\n \"shellCommandTimeout\": \"Тайм-аут shell-команды (с)\",\n \"shellCommandTimeoutTooltip\": \"Тайм-аут по умолчанию (в секундах) для инструмента execute_shell_command. LLM может переопределить это значение для каждого вызова через параметр timeout. Полезно для длительных задач, таких как компиляция или обработка данных.\",\n \"shellCommandTimeoutPlaceholder\": \"Введите тайм-аут\",\n \"shellCommandTimeoutRequired\": \"Тайм-аут shell-команды обязателен\",\n \"shellCommandTimeoutMin\": \"Тайм-аут shell-команды должен быть не менее 1 секунды\",\n \"shellCommandExecutable\": \"Исполняемый файл shell\",\n \"shellCommandExecutableTooltip\": \"Путь к shell, используемому execute_shell_command. Linux/macOS: например, /bin/bash, /bin/zsh. Windows: поддерживаются powershell.exe, pwsh.exe или POSIX-совместимые shell (например, Git Bash). Если пусто, используется переменная окружения $SHELL, затем платформенный shell по умолчанию (/bin/sh для Unix, cmd.exe для Windows).\",\n \"shellCommandExecutablePlaceholder\": \"напр. /bin/bash или powershell.exe (пусто = автоопределение)\",\n \"maxInputLength\": \"Макс. длина входа\",\n \"maxInputLengthTooltip\": \"Максимальная длина входа (токены) для окна контекста модели\",\n \"maxInputLengthPlaceholder\": \"Введите макс. длину входа\",\n \"maxInputLengthRequired\": \"Требуется указать макс. длину входа\",\n \"maxInputLengthMin\": \"Макс. длина входа должна быть не меньше 1000\",\n \"contextCompactRatio\": \"Коэффициент сжатия контекста\",\n \"contextCompactRatioTooltip\": \"Доля контекста для сжатия при заполнении\",\n \"contextCompactRatioRequired\": \"Требуется указать коэффициент сжатия контекста\",\n \"contextCompactThreshold\": \"Порог сжатия контекста\",\n \"contextCompactThresholdTooltip\": \"Порог сжатия контекста (токены), автоматически вычисляется из макс. длины входа и коэффициента сжатия\",\n \"contextCompactThresholdPlaceholder\": \"Автоматически\",\n \"contextCompactReserveRatio\": \"Коэффициент резерва контекста\",\n \"contextCompactReserveRatioTooltip\": \"Доля контекста для резерва при сжатии\",\n \"contextCompactReserveRatioRequired\": \"Требуется указать коэффициент резерва контекста\",\n \"contextCompactReserveThreshold\": \"Порог резерва контекста\",\n \"contextCompactReserveThresholdTooltip\": \"Порог резерва контекста (токены), автоматически вычисляется из макс. длины входа и коэффициента резерва\",\n \"contextCompactReserveThresholdPlaceholder\": \"Автоматически\",\n \"toolResultCompactRecentN\": \"recent_n результатов инструментов\",\n \"toolResultCompactRecentNTooltip\": \"Количество результатов инструментов для использования порога недавних сообщений\",\n \"toolResultCompactRecentNRequired\": \"recent_n результатов инструментов обязательно\",\n \"toolResultCompactOldThreshold\": \"Макс. символов для результатов инструментов за пределами recent_n\",\n \"toolResultCompactOldThresholdTooltip\": \"Результаты инструментов за пределами recent_n, превышающие этот лимит, будут сжаты\",\n \"toolResultCompactOldThresholdPlaceholder\": \"Введите порог символов\",\n \"toolResultCompactOldThresholdRequired\": \"Макс. символов для результатов за пределами recent_n обязательно\",\n \"toolResultCompactRecentThreshold\": \"Макс. символов для результатов инструментов в пределах recent_n\",\n \"toolResultCompactRecentThresholdTooltip\": \"Результаты инструментов в пределах recent_n, превышающие этот лимит, будут сжаты. Также задаёт максимальное количество байт, возвращаемых за один вызов read_file. Должно быть больше или равно порогу для старых сообщений\",\n \"toolResultCompactRecentThresholdPlaceholder\": \"Введите порог символов\",\n \"toolResultCompactRecentThresholdRequired\": \"Макс. символов для результатов в пределах recent_n обязательно\",\n \"toolResultCompactRetentionDays\": \"Дни хранения файлов результатов инструментов\",\n \"toolResultCompactRetentionDaysTooltip\": \"Количество дней хранения сжатых файлов, просроченные файлы удаляются автоматически\",\n \"toolResultCompactRetentionDaysRequired\": \"Дни хранения файлов результатов инструментов обязательны\",\n \"historyRetentionDays\": \"Дни хранения истории\",\n \"historyRetentionDaysTooltip\": \"Количество дней хранения постоянной истории чата. Более старые записи автоматически удаляются при запуске и завершении работы агента.\",\n \"historyRetentionDaysRequired\": \"Дни хранения истории обязательны\",\n \"historyRetentionDaysForeverWarning\": \"Значение 0 хранит историю вечно — объём хранилища будет расти без ограничений.\",\n \"historyRetentionDaysLargeWarning\": \"Большой период хранения может занимать значительный объём хранилища.\",\n \"exemptFileExtensions\": \"Исключенные расширения файлов\",\n \"exemptFileExtensionsTooltip\": \"Результаты read_file для этих расширений будут использовать порог для новых сообщений\",\n \"exemptFileExtensionsPlaceholder\": \".md, .txt, .json\",\n \"exemptToolNames\": \"Исключенные инструменты\",\n \"exemptToolNamesTooltip\": \"Результаты этих инструментов будут использовать порог для новых сообщений\",\n \"exemptToolNamesPlaceholder\": \"chat_with_agent, list_agents\",\n \"language\": \"Язык агента\",\n \"languageTooltip\": \"Язык файлов персонажа агента (SOUL.md, AGENTS.md и др.). При смене языка MD-файлы будут скопированы на выбранном языке.\",\n \"languageConfirmTitle\": \"Смена языка агента\",\n \"languageConfirmContent\": \"При смене языка следующие файлы будут перезаписаны версией на новом языке:\\n\\n SOUL.md, AGENTS.md, PROFILE.md, MEMORY.md, BOOTSTRAP.md, HEARTBEAT.md\\n\\nЕсли вы вносили изменения в эти файлы, сначала сделайте резервную копию. Остальные файлы не будут затронуты.\",\n \"languageConfirmOk\": \"Сменить язык\",\n \"languageSaveSuccess\": \"Язык успешно обновлён\",\n \"languageSaveSuccessWithFiles\": \"Язык обновлён, скопировано файлов: {{count}}\",\n \"languageSaveFailed\": \"Не удалось обновить язык\",\n \"timezone\": \"Часовой пояс пользователя\",\n \"timezoneTooltip\": \"Используется для запланированных задач, отображения времени и контекста агента. По умолчанию — системный часовой пояс.\",\n \"timezoneRequired\": \"Пожалуйста, выберите часовой пояс\",\n \"selectTimezone\": \"Выберите часовой пояс\",\n \"timezoneSaveSuccess\": \"Часовой пояс обновлён\",\n \"timezoneSaveFailed\": \"Не удалось обновить часовой пояс\",\n \"saveSuccess\": \"Конфигурация успешно сохранена\",\n \"saveFailed\": \"Не удалось сохранить конфигурацию\",\n \"loadFailed\": \"Не удалось загрузить конфигурацию\",\n \"historyMaxLength\": \"Макс. длина истории\",\n \"historyMaxLengthTooltip\": \"Максимальная длина вывода команды /history (в символах)\",\n \"historyMaxLengthRequired\": \"Требуется указать макс. длину истории\",\n \"historyMaxLengthMin\": \"Макс. длина истории должна быть не меньше 1000\",\n \"historyMaxLengthPlaceholder\": \"Введите макс. длину\",\n \"contextCompactTitle\": \"Настройки сжатия контекста\",\n \"contextCompactEnabled\": \"Включить сжатие контекста\",\n \"contextCompactEnabledTooltip\": \"Автоматически сжимать контекст для предотвращения переполнения окна контекста\",\n \"tokenCountModel\": \"Модель для подсчёта токенов\",\n \"tokenCountModelTooltip\": \"Имя модели для точного подсчёта токенов; установите \\\"default\\\" для использования встроенной оценки\",\n \"tokenCountModelPlaceholder\": \"Имя модели или default\",\n \"tokenCountUseMirror\": \"Использовать зеркало HuggingFace\",\n \"tokenCountUseMirrorTooltip\": \"Использовать зеркальный сайт HuggingFace при загрузке токенизаторов\",\n \"tokenCountEstimateDivisor\": \"Делитель оценки byte/Token\",\n \"tokenCountEstimateDivisorTooltip\": \"Делитель для оценки токенов на основе байтов (токены ≈ байты / делитель). Рекомендуемое значение: 3.75–4.\",\n \"tokenCountEstimateDivisorRequired\": \"Требуется указать делитель оценки\",\n \"tokenCountEstimateDivisorMin\": \"Делитель оценки должен быть от 2 до 5\",\n \"toolResultCompactTitle\": \"Настройки сжатия результатов инструментов\",\n \"toolResultCompactEnabled\": \"Включить сжатие результатов инструментов\",\n \"toolResultCompactEnabledTooltip\": \"Сжимать слишком длинные результаты вызовов инструментов для экономии пространства контекста\",\n \"memorySummaryTitle\": \"Настройки долгосрочной памяти\",\n \"memorySummaryEnabled\": \"Запустить сводку памяти\",\n \"memorySummaryEnabledTooltip\": \"Автоматически генерировать и сохранять сводки памяти при сжатии контекста\",\n \"dreamCronEnabled\": \"Включить сновидения\",\n \"dreamCronEnabledTooltip\": \"Включить плановое задание оптимизации памяти через сновидения\",\n \"dreamCron\": \"Расписание сновидений\",\n \"dreamCronTooltip\": \"Cron-выражение для периодической оптимизации долгосрочных воспоминаний через состояние сновидения; используйте переключатель выше для включения или отключения\",\n \"dreamCronPlaceholder\": \"0 23 * * *\",\n \"dreamCronRequired\": \"Введите Cron-выражение, если сновидения включены\",\n \"dreamCronInvalid\": \"Введите допустимое Cron-выражение из 5 полей\",\n \"autoMemorySearch\": \"Автоматический поиск по памяти (Beta)\",\n \"autoMemorySearchTooltip\": \"Автоматически запускать поиск по памяти при каждом диалоге. Каждый запрос будет автоматически вызывать функцию поиска по памяти, помогая QwenPaw лучше понять вас — но это увеличивает расход токенов.\",\n \"autoMaxResults\": \"Макс. результатов автоматического поиска\",\n \"autoMaxResultsTooltip\": \"Максимальное количество результатов при включённом автоматическом поиске по памяти\",\n \"autoMaxResultsRequired\": \"Требуется указать макс. число результатов\",\n \"autoMaxResultsMin\": \"Макс. число результатов должно быть не меньше 1\",\n \"inboxPushEnabled\": \"Уведомления о результатах памяти\",\n \"inboxPushEnabledTooltip\": \"Отправлять результаты автоматической памяти, сновидений и ресурсов во входящие. Отключение не останавливает задачи памяти.\",\n \"autoMemoryInterval\": \"Интервал автоматической памяти\",\n \"autoMemoryIntervalTooltip\": \"Запускать автоматическую память каждые N пользовательских запросов. 0 отключает периодическую автоматическую память. 1 означает запуск после каждого запроса. Рекомендуемое значение: 2-5; при значении 1 учитывайте высокий расход токенов.\",\n \"autoMemoryIntervalPlaceholder\": \"Введите 0, чтобы отключить периодическую автоматическую память\",\n \"autoMemoryIntervalRequired\": \"Требуется указать интервал автоматической памяти\",\n \"autoMemoryIntervalMin\": \"Интервал автоматической памяти должен быть не меньше 0\",\n \"embeddingConfigTitle\": \"Настройки модели эмбеддинга\",\n \"embeddingBackend\": \"Тип SDK эмбеддинга\",\n \"embeddingBackendTooltip\": \"Тип SDK для вызова модели эмбеддинга\",\n \"embeddingBackendPlaceholder\": \"Выберите тип SDK\",\n \"embeddingApiKey\": \"API Key\",\n \"embeddingApiKeyTooltip\": \"API-ключ провайдера эмбеддинга; оставьте пустым, если аутентификация не требуется\",\n \"embeddingApiKeyPlaceholder\": \"Оставьте пустым, если не требуется\",\n \"embeddingBaseUrl\": \"Base URL\",\n \"embeddingBaseUrlTooltip\": \"Base URL API эмбеддинга. Для OpenAI-совместимых бэкендов этот пользовательский endpoint необязателен; API Key и название модели обязательны.\",\n \"embeddingBaseUrlPlaceholder\": \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n \"embeddingHost\": \"Host\",\n \"embeddingHostTooltip\": \"Адрес сервиса Ollama. Если оставить пустым, будет использован хост Ollama по умолчанию.\",\n \"embeddingHostPlaceholder\": \"http://localhost:11434\",\n \"embeddingModelName\": \"Название модели\",\n \"embeddingModelNameTooltip\": \"Название модели эмбеддинга. Обязательно для всех бэкендов эмбеддинга.\",\n \"embeddingModelNamePlaceholder\": \"text-embedding-v4\",\n \"embeddingEnableCache\": \"Включить кэш эмбеддинга\",\n \"embeddingEnableCacheTooltip\": \"Кэшировать результаты эмбеддинга для уменьшения повторных вычислений\",\n \"embeddingMaxCacheSize\": \"Макс. размер кэша\",\n \"embeddingMaxCacheSizeTooltip\": \"Максимальное количество записей в кэше эмбеддинга\",\n \"embeddingMaxCacheSizeRequired\": \"Требуется указать макс. размер кэша\",\n \"embeddingUseDimensions\": \"Использовать пользовательские размерности\",\n \"embeddingUseDimensionsTooltip\": \"Использовать пользовательские размерности эмбеддинга (поддерживается только некоторыми моделями)\",\n \"embeddingDimensions\": \"Размерности эмбеддинга\",\n \"embeddingDimensionsTooltip\": \"Количество размерностей эмбеддинга; вступает в силу при включённых пользовательских размерностях\",\n \"embeddingDimensionsRequired\": \"Требуется указать размерности эмбеддинга\",\n \"embeddingDimensionsMin\": \"Размерности должны быть не меньше 1\",\n \"embeddingMaxInputLength\": \"Макс. длина входа\",\n \"embeddingMaxInputLengthTooltip\": \"Максимальная длина входа (токены) для одного запроса эмбеддинга\",\n \"embeddingMaxInputLengthRequired\": \"Требуется указать макс. длину входа\",\n \"embeddingMaxBatchSize\": \"Макс. размер пакета\",\n \"embeddingMaxBatchSizeTooltip\": \"Максимальное количество элементов в одном пакетном запросе эмбеддинга\",\n \"embeddingMaxBatchSizeRequired\": \"Требуется указать макс. размер пакета\",\n \"embeddingRestartWarning\": \"Настройки модели эмбеддинга не поддерживают горячую перезагрузку. Сохраните и перезапустите сервис для применения изменений.\",\n \"maxContextLength\": \"Макс. длина контекста\",\n \"maxContextLengthTooltip\": \"Максимальное количество токенов в окне контекста модели, определяющее, сколько истории помещается в каждый запрос\",\n \"maxContextLengthRequired\": \"Требуется указать макс. длину контекста\",\n \"maxContextLengthMin\": \"Макс. длина контекста должна быть не меньше 1000\",\n \"maxContextLengthPlaceholder\": \"Введите макс. длину контекста\",\n \"embeddingActivationNote\": \"Эмбеддинг активируется, когда заполнены обязательные поля выбранного бэкенда.\",\n \"embeddingEnableHint\": \"Эмбеддинг активируется, когда заполнены обязательные поля: название модели и API Key для OpenAI-совместимых или Gemini бэкендов; название модели для Ollama.\",\n \"rebuildMemoryIndex\": \"Перестроить индекс памяти\",\n \"rebuildMemoryIndexWarning\": \"Эта операция очищает и перестраивает поисковый индекс памяти и может потреблять много ресурсов CPU и оперативной памяти. Используйте её только для восстановления повреждённого индекса или исправления некорректных результатов поиска.\",\n \"rebuildMemoryIndexConfirmTitle\": \"Перестроить индекс памяти?\",\n \"rebuildMemoryIndexConfirm\": \"Во время перестроения нагрузка на CPU и потребление оперативной памяти могут значительно возрасти. Операция не рекомендуется, если индекс не повреждён и не нуждается в восстановлении.\",\n \"rebuildMemoryIndexSuccess\": \"Индекс памяти успешно перестроен\",\n \"rebuildMemoryIndexFailed\": \"Не удалось перестроить индекс памяти: {{error}}\",\n \"toolResultCompactRecentGtOld\": \"Порог для последних сообщений должен быть больше или равен порогу для старых сообщений\",\n \"lightContextTitle\": \"Управление контекстом\",\n \"visualCompactCollapseLabel\": \"Визуальное сжатие\",\n \"visualCompactDescription\": \"Сжимает старый и длинный контекст в визуальные страницы, сокращая входные токены. Недавний диалог остаётся текстом.\",\n \"visualCompactEnabled\": \"Включить Visual Compact\",\n \"visualCompactEffort\": \"Интенсивность сжатия\",\n \"visualCompactLow\": \"Низкая\",\n \"visualCompactMedium\": \"Средняя\",\n \"visualCompactHigh\": \"Высокая\",\n \"visualCompactLowDescription\": \"Сжимает меньше содержимого и отдаёт приоритет читаемости.\",\n \"visualCompactMediumDescription\": \"Балансирует экономию токенов и визуальную читаемость.\",\n \"visualCompactHighDescription\": \"Сжимает больше подходящего контекста и отдаёт приоритет экономии токенов.\",\n \"visualCompactQualityNote\": \"Более сильное сжатие не обязательно повышает качество ответа.\",\n \"visualCompactCapabilityNote\": \"Работает только с моделями, поддерживающими ввод изображений.\",\n \"remeLightMemoryTitle\": \"Долгосрочная память\",\n \"toolExecutionLevelTitle\": \"Безопасность выполнения инструментов\",\n \"toolExecutionLevel\": {\n \"title\": \"Безопасность выполнения инструментов\",\n \"alertMessage\": \"Настройте политику одобрения вызовов инструментов для контроля уровня безопасности при выполнении инструментов агентом\",\n \"strict\": \"Строгий режим\",\n \"strictDesc\": \"Все вызовы инструментов требуют одобрения, максимальный уровень безопасности\",\n \"smart\": \"Умный режим\",\n \"smartDesc\": \"Инструменты с низким риском одобряются автоматически, средний+ риск требует одобрения\",\n \"auto\": \"Автоматический режим\",\n \"autoDesc\": \"Только инструменты, явно отмеченные для одобрения, требуют одобрения (по умолчанию)\",\n \"off\": \"Выключено\",\n \"offDesc\": \"Отключить все одобрения инструментов, все инструменты выполняются автоматически\"\n },\n \"saveLevelSuccess\": \"Уровень безопасности выполнения инструментов сохранён\",\n \"saveLevelFailed\": \"Не удалось сохранить уровень безопасности выполнения инструментов\",\n \"adbpgMemoryTitle\": \"ADBPG Long-term Memory\",\n \"adbpgConfig\": {\n \"title\": \"ADBPG Memory Configuration\",\n \"restBaseUrl\": \"REST Base URL\",\n \"restApiKey\": \"REST API Key\",\n \"memoryIsolation\": \"Memory Isolation (per-agent)\",\n \"searchTimeout\": \"Search Timeout\"\n }\n },\n \"tools\": {\n \"title\": \"Встроенные инструменты\",\n \"description\": \"Управляйте встроенными инструментами и их статусом. Отключённые инструменты не будут доступны агенту.\",\n \"emptyState\": \"Нет настроенных инструментов\",\n \"loadError\": \"Не удалось загрузить инструменты\",\n \"enableSuccess\": \"Инструмент успешно включён\",\n \"disableSuccess\": \"Инструмент успешно отключён\",\n \"toggleError\": \"Не удалось изменить статус инструмента\",\n \"enableAll\": \"Включить все\",\n \"disableAll\": \"Отключить все\",\n \"enableAllSuccess\": \"Все инструменты включены\",\n \"disableAllSuccess\": \"Все инструменты отключены\",\n \"allEnabled\": \"Все инструменты уже включены\",\n \"allDisabled\": \"Все инструменты уже отключены\",\n \"asyncExecution\": \"Асинхронное выполнение\",\n \"asyncExecutionEnabled\": \"Асинхронное выполнение включено\",\n \"asyncExecutionDisabled\": \"Асинхронное выполнение отключено\",\n \"browserUnifiedMode\": \"Унифицированный браузер (бета)\",\n \"browserLegacyMode\": \"Режим совместимости с устаревшей версией\",\n \"browserUnifiedModeButton\": \"Новый (бета)\",\n \"browserLegacyModeButton\": \"Старый (совм.)\",\n \"browserUnifiedDescription\": \"Использовать новые возможности унифицированного браузера.\",\n \"browserLegacyDescription\": \"Использовать исходную реализацию браузера для прежнего поведения.\",\n \"browserRestartPending\": \"После перезапуска служба переключится на: {{mode}}\",\n \"configure\": \"Настроить\",\n \"configured\": \"Настроено\",\n \"requiresConfig\": \"Требуется настройка\",\n \"configSaved\": \"Конфигурация сохранена\",\n \"configSaveError\": \"Не удалось сохранить конфигурацию\",\n \"active\": \"активно\",\n \"available\": \"Доступно\",\n \"noEnabled\": \"Нет включённых инструментов\",\n \"goEnableBtn\": \"Включить\",\n \"configureAction\": \"Настроить →\",\n \"enableAction\": \"Включить →\"\n },\n \"modelConfig\": {\n \"promptTitle\": \"Требуется LLM-модель — выберите в Chat после настройки\",\n \"promptMessage\": \"Для работы чата нужна LLM-модель. Сначала настройте модель, затем выберите её в селекторе моделей вверху страницы Chat.\",\n \"configureButton\": \"Настроить модель\",\n \"skipButton\": \"Пропустить\",\n \"chatDisabledTitle\": \"Чат отключён\",\n \"chatDisabledMessage\": \"Функции чата отключены, потому что не настроена модель. Настройте модель, чтобы включить чат.\",\n \"configureNow\": \"Настроить модель\",\n \"modelNotConfigured\": \"Перед использованием чата настройте модель в разделе «Настройки»\"\n },\n \"header\": {\n \"changelog\": \"Список изменений\",\n \"docs\": \"Документация\",\n \"tutorial\": \"Руководство\",\n \"featureDemos\": \"Демонстрация функций\",\n \"faq\": \"FAQ\",\n \"github\": \"GitHub\",\n \"resources\": \"Документация\",\n \"updateAvailable\": \"Доступно обновление\"\n },\n \"theme\": {\n \"dark\": \"Тёмная\",\n \"light\": \"Светлая\",\n \"system\": \"Как в системе\"\n },\n \"sidebar\": {\n \"newVersion\": \"Доступна новая версия: v{{version}}, нажмите для обновления\",\n \"updateModal\": {\n \"title\": \"Доступна новая версия: v{{version}}\",\n \"viewReleases\": \"Открыть релизы\",\n \"installDesktopUpdate\": \"Обновить сейчас\",\n \"desktopInstallHint\": \"QwenPaw Desktop {{version}} готов к установке. Приложение перезапустится после обновления.\",\n \"checking\": \"Проверка обновлений…\",\n \"checkingHint\": \"Проверяем последнюю версию\",\n \"downloading\": \"Загрузка обновления\",\n \"downloadingTo\": \"Обновление до версии {{version}}\",\n \"downloadProgress\": \"{{done}} / {{total}} · {{rate}}\",\n \"installing\": \"Установка обновления\",\n \"willRestart\": \"Приложение перезапустится автоматически после установки.\",\n \"stepPrepare\": \"Подготовка\",\n \"stepDownloading\": \"Загрузка\",\n \"stepInstalling\": \"Установка\",\n \"failedTitle\": \"Обновление не удалось\",\n \"back\": \"Назад\",\n \"retry\": \"Повторить\",\n \"errors\": {\n \"network\": \"Не удалось подключиться к серверу обновлений. Проверьте интернет и повторите попытку.\",\n \"signature\": \"Не удалось проверить подпись файла обновления. Попробуйте загрузить ещё раз.\",\n \"appLocation\": \"QwenPaw запущен из macOS-пути только для чтения. Переместите QwenPaw.app в папку Applications, запустите его оттуда и повторите обновление.\",\n \"other\": \"Обновление не удалось.\"\n },\n \"updateLater\": \"Скачать в фоне\",\n \"backgroundDownloading\": \"Загрузка в фоновом режиме…\",\n \"readyToInstall\": \"Обновление готово\",\n \"readyToInstallHint\": \"Обновление v{{version}} загружено. Перезапустите приложение, чтобы установить и открыть новую версию.\",\n \"restartNow\": \"Обновить сейчас\",\n \"backgroundFailed\": \"Фоновая загрузка не удалась\"\n },\n \"settings\": {\n \"language\": \"Язык\",\n \"theme\": \"Тема\",\n \"mode\": \"Режим\"\n },\n \"simpleMode\": \"Простой режим\",\n \"fullMode\": \"Полный режим\"\n },\n \"chat\": {\n \"modelSelectTooltip\": \"Выбор модели\",\n \"newChatTooltip\": \"Новый чат\",\n \"searchTooltip\": \"Поиск во всех чатах\",\n \"chatHistoryTooltip\": \"История чатов\",\n \"wideModeTooltip\": \"Широкий режим\",\n \"normalModeTooltip\": \"Обычный режим\",\n \"allChats\": \"Все чаты\",\n \"createNewChat\": \"Создать новый чат\",\n \"pinDrawer\": \"Закрепить\",\n \"unpinDrawer\": \"Открепить\",\n \"statusIdle\": \"Ожидание\",\n \"statusInProgress\": \"Выполняется\",\n \"disclaimer\": \"Работает для вас, растёт вместе с вами\",\n \"greeting\": \"Привет, чем я могу помочь вам сегодня?\",\n \"description\": \"Я полезный ассистент, который поможет вам с вашими вопросами.\",\n \"prompt1\": \"Давайте начнём новое путешествие!\",\n \"prompt2\": \"Расскажи мне, какими навыками ты обладаешь?\",\n \"inputPlaceholder\": \"\\\"↑↓\\\" для навигации по сообщениям · \\\"/\\\" для быстрых команд (\\\"/approve\\\" или \\\"/deny\\\" во время подтверждения)\",\n \"longTextUploadPrompt\": \"Пожалуйста, прочитайте загруженный файл с промптом и ответьте на его основе.\",\n \"emptyOutputError\": \"Ассистент вернул пустой ответ.\",\n \"commands\": {\n \"clear\": {\n \"description\": \"Очистить контекст диалога\"\n },\n \"compact\": {\n \"description\": \"Сжать контекст диалога и создать резюме; поддерживается доп. инструкция\"\n },\n \"approve\": {\n \"description\": \"Подтвердить ожидающее действие инструмента и продолжить выполнение\"\n },\n \"deny\": {\n \"description\": \"Отклонить ожидающее действие инструмента\"\n },\n \"mission\": {\n \"description\": \"Режим Mission — разбивает задачи на суб-агентов с изолированным контекстом\"\n },\n \"skills\": {\n \"description\": \"Показать доступные в чате навыки и явные команды их вызова\"\n },\n \"agents\": {\n \"description\": \"Показать агентов, доступных в текущей среде\"\n },\n \"review\": {\n \"description\": \"Проверить в Codex незакоммиченные изменения рабочей области\"\n },\n \"status\": {\n \"description\": \"Показать текущую учётную запись агента, модель и рабочую область\"\n },\n \"goal\": {\n \"description\": \"Goal-цикл — агент непрерывно преследует цель, автоматически оценивая завершение через Rubric\"\n }\n },\n \"attachments\": {\n \"tooltip\": \"Поддерживаются документы, изображения, видео, аудио и другие форматы\",\n \"tooltipImageOnly\": \"Текущая модель поддерживает только загрузку изображений\",\n \"tooltipNoMultimodal\": \"Мультимодальные возможности текущей модели не обнаружены, изображения или видео могут быть обработаны некорректно\",\n \"fileSizeLimit\": \"Максимум {{limit}} МБ на файл\",\n \"fileSizeExceeded\": \"Размер файла превышает лимит {{limit}} МБ. Текущий файл: {{size}} МБ\",\n \"multimodalDisabled\": \"Текущая модель не поддерживает загрузку файлов\",\n \"multimodalWarning\": \"Мультимодальные возможности текущей модели не обнаружены, изображения или видео могут быть обработаны некорректно\",\n \"imageOnlyAccept\": \"Текущая модель поддерживает только файлы изображений\",\n \"imageOnlyWarning\": \"Текущая модель поддерживает только изображения, видео и другие файлы могут быть обработаны некорректно\"\n },\n \"rateLimitHit\": \"Текущая модель ограничена. Попробуйте переключиться на другую бесплатную модель.\",\n \"turnUsagePopover\": {\n \"ariaLabel\": \"Показать использование токенов и контекста за ход\",\n \"turn\": \"Этот ход\",\n \"turnEstimated\": \"Этот ход ~\",\n \"tok\": \"tok\",\n \"inOut\": \"in {{inTok}} · out {{outTok}}\",\n \"contextLabel\": \"Окно контекста:\",\n \"manageContext\": \"Управление контекстом\",\n \"compact\": \"Сжать\",\n \"new\": \"Новый чат\"\n },\n \"rateLimitMessage\": \"Достигнут лимит запросов. Переключитесь на другую бесплатную модель:\",\n \"rateLimitSwitched\": \"Переключено на {{model}}\",\n \"speech\": {\n \"startRecording\": \"Начать запись голоса (Ctrl+Shift+M)\",\n \"stopRecording\": \"Остановить запись (Ctrl+Shift+M)\",\n \"transcribing\": \"Транскрибация...\",\n \"transcriptionDisabled\": \"Транскрибация отключена. Настройте провайдер в Настройки > Голосовая транскрибация.\",\n \"transcriptionFailed\": \"Ошибка голосовой транскрибации\",\n \"microphoneError\": \"Не удалось получить доступ к микрофону\",\n \"fileTooLarge\": \"Файл записи слишком большой ({{size}}МБ). Максимум: {{limit}}МБ\",\n \"recordingTooLong\": \"Длительность записи превышает лимит ({{limit}}с)\"\n },\n \"search\": {\n \"title\": \"Поиск в чате\",\n \"tooltip\": \"Поиск в текущем чате\",\n \"placeholder\": \"Поиск сообщений...\",\n \"noResults\": \"Результаты не найдены\",\n \"resultsCount\": \"Найдено {{count}} результат(ов)\",\n \"searching\": \"Поиск {{progress}}...\",\n \"loading\": \"Загрузка...\",\n \"userMessage\": \"Пользователь\",\n \"assistantMessage\": \"Ассистент\",\n \"titleMatch\": \"Название\"\n },\n \"contextMenu\": {\n \"open\": \"Открыть\",\n \"rename\": \"Переименовать\",\n \"pin\": \"Закрепить\",\n \"unpin\": \"Открепить\",\n \"delete\": \"Удалить\"\n },\n \"queue\": {\n \"title\": \"Очередь сообщений\",\n \"paused\": \"Приостановлено\",\n \"sendFailed\": \"Ошибка отправки\",\n \"resume\": \"Возобновить очередь\",\n \"pause\": \"Приостановить очередь\",\n \"clear\": \"Очистить очередь\",\n \"dragSort\": \"Перетащите для сортировки\",\n \"edit\": \"Редактировать\",\n \"retry\": \"Повторить\",\n \"skip\": \"Пропустить\",\n \"interruptAndSend\": \"Прервать и отправить\",\n \"delete\": \"Удалить\",\n \"file\": \"Файл\",\n \"noChatWarning\": \"Сначала отправьте сообщение, чтобы создать диалог, прежде чем использовать очередь\",\n \"queueFull\": \"Очередь полна (макс. {{max}} записей). Отправьте или удалите некоторые сообщения\",\n \"otherTabOwner\": \"This tab queues only; sending is handled by another tab\"\n },\n \"group\": {\n \"pinned\": \"Закреплённые\",\n \"today\": \"Сегодня\",\n \"week\": \"За 7 дней\",\n \"month\": \"За 30 дней\",\n \"older\": \"Ранее\"\n },\n \"sessionPanel\": {\n \"searchConversations\": \"Поиск…\",\n \"noResults\": \"Результатов нет\",\n \"noConversations\": \"Нет диалогов\",\n \"loadingConversations\": \"Загрузка…\"\n },\n \"conversationHistory\": \"История диалогов\",\n \"history\": {\n \"empty\": \"Нет истории чатов\"\n },\n \"sessionGroup\": {\n \"today\": \"Сегодня\",\n \"recent\": \"За 7 дней\",\n \"earlier\": \"Ранее\"\n }\n },\n \"security\": {\n \"parent\": \"Настройки\",\n \"security\": \"Безопасность\",\n \"title\": \"Безопасность\",\n \"description\": \"Управление функциями безопасности инструментов и навыков.\",\n \"toolGuardTitle\": \"Защита инструментов\",\n \"toolGuardDescription\": \"Настройте сканирование безопасности для вызовов инструментов. Опасные операции потребуют вашего явного подтверждения перед выполнением.\",\n \"enabled\": \"Включить защиту инструментов\",\n \"enabledTooltip\": \"При включении вызовы инструментов сканируются на наличие опасных шаблонов перед выполнением\",\n \"guardedTools\": \"Защищённые инструменты\",\n \"guardedToolsTooltip\": \"Инструменты, требующие подтверждения при обнаружении опасных шаблонов. Оставьте пустым для использования набора по умолчанию.\",\n \"guardedToolsPlaceholder\": \"Выберите инструменты или введите пользовательские имена\",\n \"deniedTools\": \"Запрещённые инструменты\",\n \"deniedToolsTooltip\": \"Инструменты, которые всегда отклоняются без запроса подтверждения\",\n \"deniedToolsPlaceholder\": \"Выберите инструменты для запрета\",\n \"saveSuccess\": \"Настройки защиты инструментов сохранены\",\n \"saveFailed\": \"Не удалось сохранить настройки защиты инструментов\",\n \"loadFailed\": \"Не удалось загрузить настройки защиты инструментов\",\n \"rules\": {\n \"title\": \"Правила обнаружения\",\n \"description\": \"Правила определяют шаблоны регулярных выражений, которые вызывают оповещения безопасности. Встроенные правила можно отключить; пользовательские правила можно добавлять, редактировать или удалять.\",\n \"id\": \"ID правила\",\n \"severity\": \"Серьёзность\",\n \"descriptionCol\": \"Описание\",\n \"source\": \"Источник\",\n \"actions\": \"Действия\",\n \"builtin\": \"Встроенное\",\n \"custom\": \"Пользовательское\",\n \"enable\": \"Включить\",\n \"disable\": \"Отключить\",\n \"edit\": \"Редактировать\",\n \"delete\": \"Удалить\",\n \"add\": \"Добавить правило\",\n \"addTitle\": \"Добавить пользовательское правило\",\n \"editTitle\": \"Редактировать пользовательское правило\",\n \"ruleId\": \"ID правила\",\n \"ruleIdRequired\": \"ID правила обязателен\",\n \"duplicateId\": \"Правило с таким ID уже существует\",\n \"tools\": \"Целевые инструменты\",\n \"toolsPlaceholder\": \"Оставьте пустым для совпадения со всеми инструментами\",\n \"params\": \"Целевые параметры\",\n \"paramsPlaceholder\": \"Оставьте пустым для совпадения со всеми параметрами\",\n \"severityLabel\": \"Серьёзность\",\n \"categoryLabel\": \"Категория\",\n \"patterns\": \"Шаблоны регулярных выражений\",\n \"patternsRequired\": \"Требуется хотя бы один шаблон\",\n \"patternsTooltip\": \"По одному регулярному выражению в строке. Сопоставляется со значениями параметров инструмента (без учёта регистра).\",\n \"excludePatterns\": \"Шаблоны исключения\",\n \"excludePatternsTooltip\": \"По одному регулярному выражению в строке. Если совпадает, правило пропускается.\",\n \"descriptionLabel\": \"Описание\",\n \"descriptionPlaceholder\": \"Что обнаруживает это правило?\",\n \"remediationLabel\": \"Рекомендация\",\n \"remediationPlaceholder\": \"Рекомендуемое действие при срабатывании правила\",\n \"preview\": \"Просмотр\",\n \"previewTitle\": \"Детали правила\",\n \"actionLabel\": \"Действие\",\n \"actionApproval\": \"Требуется одобрение\",\n \"autoDeny\": \"Автоотказ\",\n \"autoDenyTooltip\": \"При включении вызовы инструментов, соответствующие этому правилу, будут автоматически отклонены без ручного подтверждения\",\n \"autoDenyEnable\": \"Включить автоотказ для этого правила\",\n \"autoDenyDisable\": \"Отключить автоотказ для этого правила\",\n \"allTools\": \"Все инструменты\",\n \"allParams\": \"Все параметры\",\n \"descriptions\": {\n \"TOOL_CMD_CONTROL_CHARS\": \"Обнаруживает непечатаемые управляющие символы, которые могут обойти проверки безопасности\",\n \"TOOL_CMD_DANGEROUS_MV\": \"Обнаруживает команду mv, которая может неожиданно переместить или перезаписать файлы\",\n \"TOOL_CMD_DANGEROUS_RM\": \"Обнаруживает команду rm, которая может привести к потере данных\",\n \"TOOL_CMD_DOS_FORK_BOMB\": \"Обнаруживает классические Bash fork-бомбы и массовое завершение процессов\",\n \"TOOL_CMD_FIND_DELETE\": \"Обнаруживает команду 'find -delete', которая может привести к потере данных\",\n \"TOOL_CMD_FS_DESTRUCTION\": \"Обнаруживает команды низкоуровневого форматирования или очистки дисков\",\n \"TOOL_CMD_IFS_INJECTION\": \"Обнаруживает команды с переменной $IFS, которая может обойти проверку безопасности\",\n \"TOOL_CMD_JQ_FILE_FLAGS\": \"Обнаруживает команды jq с флагами для чтения произвольных файлов или выполнения кода\",\n \"TOOL_CMD_JQ_SYSTEM\": \"Обнаруживает команды jq с функцией system() для выполнения произвольных команд\",\n \"TOOL_CMD_OBFUSCATED_EXEC\": \"Обнаруживает выполнение строк в кодировке base64, передаваемых напрямую в интерпретатор оболочки\",\n \"TOOL_CMD_PIPE_TO_SHELL\": \"Обнаруживает паттерны 'curl | bash' для загрузки и немедленного выполнения удалённых скриптов\",\n \"TOOL_CMD_PRIVILEGE_ESCALATION\": \"Обнаруживает попытки повышения привилегий с использованием sudo, su, doas, pkexec или runas\",\n \"TOOL_CMD_PROCESS_KILL\": \"Обнаруживает команды завершения процессов, которые могут остановить критические процессы\",\n \"TOOL_CMD_PROC_ENVIRON\": \"Обнаруживает доступ к /proc/*/environ, который может раскрыть переменные окружения\",\n \"TOOL_CMD_REVERSE_SHELL\": \"Обнаруживает попытки создания обратных оболочек или несанкционированных сетевых туннелей\",\n \"TOOL_CMD_SERVICE_RESTART\": \"Обнаруживает команды управления службами, которые могут нарушить работу системных служб\",\n \"TOOL_CMD_SYSTEM_REBOOT\": \"Обнаруживает команды перезагрузки или выключения системы, которые остановят хост-систему\",\n \"TOOL_CMD_SYSTEM_TAMPERING\": \"Обнаруживает доступ к заданиям cron, SSH-ключам или правам sudo (включая чтение и изменение)\",\n \"TOOL_CMD_UNICODE_WHITESPACE\": \"Обнаруживает пробельные символы Unicode, вызывающие неоднозначность парсинга\",\n \"TOOL_CMD_UNSAFE_PERMISSIONS\": \"Обнаруживает глобальное понижение прав доступа (chmod 777) или установку неизменяемых флагов\",\n \"TOOL_CMD_ZSH_DANGEROUS\": \"Обнаруживает Zsh-специфичные встроенные команды, которые могут обойти проверки безопасности\"\n },\n \"categories\": {\n \"command_injection\": \"Внедрение команд\",\n \"code_execution\": \"Выполнение кода\",\n \"data_exfiltration\": \"Утечка данных\",\n \"path_traversal\": \"Обход пути\",\n \"sensitive_file_access\": \"Доступ к конфиденциальным файлам\",\n \"network_abuse\": \"Злоупотребление сетью\",\n \"credential_exposure\": \"Утечка учётных данных\",\n \"resource_abuse\": \"Злоупотребление ресурсами\",\n \"privilege_escalation\": \"Повышение привилегий\",\n \"prompt_injection\": \"Внедрение промпта\",\n \"other\": \"Другое\"\n }\n },\n \"fileGuard\": {\n \"title\": \"Защита файлов\",\n \"description\": \"Защита конфиденциальных файлов и директорий от доступа инструментов агента. Добавленные пути будут заблокированы для всех вызовов инструментов.\",\n \"enableLabel\": \"Включить защиту файлов\",\n \"path\": \"Путь\",\n \"actions\": \"Действия\",\n \"add\": \"Добавить\",\n \"inputPlaceholder\": \"Введите путь к файлу или директории (например, ~/.ssh/ или /etc/passwd)\",\n \"empty\": \"Пользовательские конфиденциальные пути не настроены\",\n \"directory\": \"Директория\",\n \"removeConfirm\": \"Удалить этот путь из списка защиты?\",\n \"duplicate\": \"Этот путь уже есть в списке\",\n \"saveSuccess\": \"Настройки защиты файлов сохранены\",\n \"saveFailed\": \"Не удалось сохранить настройки защиты файлов\",\n \"loadFailed\": \"Не удалось загрузить настройки защиты файлов\",\n \"allowPreviewOutsideWorkspace\": \"Разрешить предпросмотр файлов вне рабочего пространства\",\n \"allowPreviewOutsideWorkspaceDesc\": \"Разрешить предпросмотр файлов за пределами рабочей директории в консоли. Защита конфиденциальных файлов остаётся активной.\"\n },\n \"allowNoAuthHosts\": {\n \"title\": \"Хосты без аутентификации\",\n \"warningTitle\": \"Предупреждение безопасности\",\n \"warningDescription\": \"IP-адреса в этом списке могут обращаться к конечным точкам API без аутентификации. По умолчанию разрешены локальные хосты (127.0.0.1 и ::1) для доступа через CLI. Добавляйте только доверенные IP-адреса. ВНИМАНИЕ: Добавление недоверенных IP представляет серьезную угрозу безопасности.\",\n \"ipAddress\": \"IP-адрес\",\n \"actions\": \"Действия\",\n \"add\": \"Добавить\",\n \"inputPlaceholder\": \"Введите IP-адрес (например, 192.168.1.100 или ::1)\",\n \"empty\": \"IP-адреса не настроены\",\n \"default\": \"По умолчанию\",\n \"removeConfirm\": \"Удалить этот IP-адрес из белого списка?\",\n \"duplicate\": \"Этот IP-адрес уже есть в списке\",\n \"invalidIP\": \"Неверный формат IP-адреса\",\n \"saveSuccess\": \"Настройки хостов без аутентификации сохранены\",\n \"saveFailed\": \"Не удалось сохранить настройки хостов без аутентификации\",\n \"loadFailed\": \"Не удалось загрузить настройки хостов без аутентификации\"\n },\n \"skillScanner\": {\n \"title\": \"Сканер навыков\",\n \"description\": \"Автоматическое сканирование навыков на наличие угроз безопасности перед активацией или установкой.\",\n \"mode\": \"Режим сканирования\",\n \"modeTooltip\": \"Управление поведением сканера: блокировка, только предупреждение или отключение\",\n \"modeBlock\": \"Блокировать\",\n \"modeWarn\": \"Только предупреждение\",\n \"modeOff\": \"Выключен\",\n \"timeout\": \"Таймаут сканирования (секунды)\",\n \"timeoutTooltip\": \"Максимальное время ожидания завершения сканирования (5-300 секунд)\",\n \"saveSuccess\": \"Настройки сканера навыков сохранены\",\n \"saveFailed\": \"Не удалось сохранить настройки сканера навыков\",\n \"scanAlerts\": {\n \"title\": \"Оповещения сканирования\",\n \"empty\": \"Нет оповещений безопасности\",\n \"clearAll\": \"Очистить все\",\n \"clearConfirm\": \"Очистить все оповещения сканирования?\",\n \"skillName\": \"Навык\",\n \"action\": \"Действие\",\n \"actionBlocked\": \"Заблокировано\",\n \"actionWarned\": \"Предупреждение\",\n \"time\": \"Время\",\n \"actions\": \"Действия\",\n \"allowSkill\": \"Добавить в белый список\",\n \"remove\": \"Удалить\",\n \"viewFindings\": \"Подробности\"\n },\n \"whitelist\": {\n \"title\": \"Белый список\",\n \"empty\": \"Белый список пуст\",\n \"skillName\": \"Навык\",\n \"addedAt\": \"Добавлено\",\n \"contentHash\": \"Хеш содержимого\",\n \"actions\": \"Действия\",\n \"remove\": \"Удалить\",\n \"removeConfirm\": \"Удалить этот навык из белого списка?\",\n \"addSuccess\": \"Навык добавлен в белый список\",\n \"removeSuccess\": \"Навык удалён из белого списка\",\n \"addFailed\": \"Не удалось добавить навык в белый список\",\n \"removeFailed\": \"Не удалось удалить навык из белого списка\",\n \"removeWillDisable\": \"После удаления навык также будет отключён.\",\n \"removeAndDisabled\": \"Навык удалён из белого списка и отключён\"\n },\n \"scanError\": {\n \"title\": \"Обнаружены проблемы безопасности\",\n \"description\": \"Обнаружены следующие проблемы безопасности:\",\n \"warnDescription\": \"Обнаружены проблемы безопасности, но навык был включён (режим предупреждения):\",\n \"goToWhitelist\": \"Перейти к белому списку\",\n \"moreFindings\": \"Ещё {{count}} записей не показано (полный список: Настройки → Безопасность → Сканер навыков).\"\n }\n }\n },\n \"login\": {\n \"title\": \"Вход в QwenPaw\",\n \"registerTitle\": \"Создание аккаунта\",\n \"firstUserHint\": \"Создайте учётную запись администратора для начала работы\",\n \"usernamePlaceholder\": \"Имя пользователя\",\n \"passwordPlaceholder\": \"Пароль\",\n \"usernameRequired\": \"Пожалуйста, введите имя пользователя\",\n \"passwordRequired\": \"Пожалуйста, введите пароль\",\n \"submit\": \"Войти\",\n \"register\": \"Зарегистрироваться\",\n \"failed\": \"Ошибка входа, проверьте учётные данные\",\n \"registerSuccess\": \"Регистрация прошла успешно\",\n \"registerFailed\": \"Ошибка регистрации\",\n \"authNotEnabled\": \"Аутентификация не включена\",\n \"logout\": \"Выйти\",\n \"logoutSuccess\": \"Вы успешно вышли\",\n \"sessionExpired\": \"Сессия истекла, пожалуйста, войдите снова\",\n \"unauthorized\": \"Не авторизован, пожалуйста, войдите\"\n },\n \"account\": {\n \"title\": \"Управление аккаунтом\",\n \"currentPassword\": \"Текущий пароль\",\n \"currentPasswordRequired\": \"Пожалуйста, введите текущий пароль\",\n \"newUsername\": \"Новое имя пользователя\",\n \"newUsernamePlaceholder\": \"Оставьте пустым, чтобы не менять\",\n \"newPassword\": \"Новый пароль\",\n \"newPasswordPlaceholder\": \"Оставьте пустым, чтобы не менять\",\n \"confirmPassword\": \"Подтвердите новый пароль\",\n \"confirmPasswordPlaceholder\": \"Введите новый пароль ещё раз\",\n \"passwordMismatch\": \"Пароли не совпадают\",\n \"save\": \"Сохранить изменения\",\n \"updateSuccess\": \"Аккаунт успешно обновлён\",\n \"updateFailed\": \"Не удалось обновить аккаунт\",\n \"wrongPassword\": \"Текущий пароль неверен\",\n \"nothingToUpdate\": \"Пожалуйста, введите новое имя пользователя или пароль\",\n \"passwordEmpty\": \"Пароль не может быть пустым\",\n \"usernameEmpty\": \"Имя пользователя не может быть пустым\"\n },\n \"voiceTranscription\": {\n \"title\": \"Транскрипция голоса\",\n \"description\": \"Настройте обработку входящих аудио- и голосовых сообщений.\",\n \"loadFailed\": \"Не удалось загрузить настройки аудиорежима\",\n \"saveSuccess\": \"Аудиорежим сохранён\",\n \"saveFailed\": \"Не удалось сохранить аудиорежим\",\n \"audioModeLabel\": \"Аудиорежим\",\n \"audioModeDescription\": \"Выберите, как голосовые сообщения из каналов (Discord, Telegram и др.) обрабатываются перед отправкой модели.\",\n \"modeAuto\": \"Авто (рекомендуется)\",\n \"modeAutoDesc\": \"Транскрибировать аудио в текст через выбранный провайдер, затем отправить текст модели. Если транскрипция отключена или недоступна, отображается заглушка «файл загружен». В этом режиме аудио не отправляется модели напрямую. Работает со всеми моделями.\",\n \"modeNative\": \"Нативное аудио\",\n \"modeNativeDesc\": \"Отправлять аудиофайл модели напрямую без транскрипции. Это единственный режим, передающий аудио модели. Работает только с моделями, поддерживающими аудио (например, gpt-4o-audio). Большинство моделей не поддерживают этот режим.\",\n \"ffmpegReady\": \"ffmpeg установлен. Конвертация аудио для нативного режима доступна.\",\n \"ffmpegMissing\": \"ffmpeg не установлен.\",\n \"ffmpegMissingDesc\": \"Нативный аудиорежим требует ffmpeg для конвертации аудиоформатов (например, .ogg в .wav). Установите ffmpeg как системный пакет для включения этого режима.\",\n \"providerTypeLabel\": \"Провайдер транскрипции\",\n \"providerTypeDescription\": \"Выберите бэкенд транскрипции. Если голосовая транскрипция не нужна, выберите «Отключено».\",\n \"providerTypeDisabled\": \"Отключено\",\n \"providerTypeDisabledDesc\": \"Без транскрипции. Голосовые сообщения будут отображаться как заглушка «файл загружен».\",\n \"providerTypeWhisperApi\": \"Whisper API\",\n \"providerTypeWhisperApiDesc\": \"Использовать OpenAI-совместимый Whisper API от настроенного провайдера (например, OpenAI, Ollama).\",\n \"providerTypeLocalWhisper\": \"Локальный Whisper\",\n \"providerTypeLocalWhisperDesc\": \"Запускать транскрипцию локально с помощью библиотеки openai-whisper. Требуются ffmpeg и openai-whisper.\",\n \"localWhisperReady\": \"Локальный Whisper готов к работе. ffmpeg и openai-whisper установлены.\",\n \"localWhisperMissing\": \"Локальный Whisper не готов. Необходимо установить недостающие зависимости.\",\n \"localWhisperMissingDesc\": \"ffmpeg: {{ffmpeg}} | openai-whisper: {{whisper}}. Установите недостающие зависимости: ffmpeg (системный пакет) и openai-whisper (uv pip install openai-whisper или установите QwenPaw с опцией [whisper]).\",\n \"providerLabel\": \"Провайдер Whisper API\",\n \"providerDescription\": \"Выберите провайдера для транскрипции аудио через Whisper API. Отображаются только провайдеры с совместимым эндпоинтом.\",\n \"providerPlaceholder\": \"Выберите провайдера...\",\n \"noProvidersWarning\": \"Не найдено провайдеров для транскрипции. Настройте провайдер OpenAI для включения голосовой транскрипции.\",\n \"transcriptionInfoTitle\": \"Как работает транскрипция\",\n \"transcriptionInfoDesc\": \"Whisper API транскрипция использует OpenAI-совместимый эндпоинт /v1/audio/transcriptions. Необходим провайдер с поддержкой Whisper (например, OpenAI). Выберите конкретного провайдера выше для включения транскрипции.\",\n \"transcriptionInfoDescLocal\": \"Локальная Whisper транскрипция запускает библиотеку openai-whisper непосредственно на вашем устройстве. Требуются ffmpeg (для декодирования аудио) и пакет openai-whisper. API-ключ и подключение к интернету не нужны. Установка: uv pip install 'qwenpaw[whisper]'.\"\n },\n \"acp\": {\n \"title\": \"ACP\",\n \"loading\": \"Загрузка конфигурации ACP...\",\n \"builtin\": \"Встроенный\",\n \"custom\": \"Пользовательский\",\n \"create\": \"Добавить пользовательский агент\",\n \"createTitle\": \"Создать ACP агент\",\n \"createSuccess\": \"ACP агент создан\",\n \"editTitle\": \"Редактировать конфигурацию ACP\",\n \"agentKey\": \"Ключ агента\",\n \"agentKeyRequired\": \"Пожалуйста, введите ключ агента\",\n \"agentKeyInvalid\": \"Ключ агента может содержать только буквы, цифры, подчёркивания и дефисы\",\n \"agentKeyExists\": \"Этот ключ агента уже существует\",\n \"enabled\": \"Включено\",\n \"command\": \"Команда\",\n \"commandRequired\": \"Пожалуйста, введите команду\",\n \"args\": \"Аргументы\",\n \"argsHelp\": \"По одному аргументу на строку\",\n \"env\": \"Переменные окружения\",\n \"envHelp\": \"Используйте формат KEY=VALUE в каждой строке\",\n \"envInvalidLine\": \"Неверный формат переменной окружения: {{line}}\",\n \"trusted\": \"Доверенный\",\n \"toolParseMode\": \"Режим разбора инструментов\",\n \"toolParseModeRequired\": \"Пожалуйста, выберите режим разбора инструментов\",\n \"stdioBufferLimit\": \"Лимит буфера stdio\",\n \"stdioBufferLimitHelp\": \"Максимальный размер буфера чтения одной строки stdio для ACP-подпроцесса в байтах.\",\n \"stdioBufferLimitRequired\": \"Пожалуйста, укажите лимит буфера stdio\",\n \"stdioBufferLimitMin\": \"Лимит буфера stdio должен быть не меньше 1 байта\",\n \"docs\": \"Документация\",\n \"docsHelp\": \"Открыть документацию по ACP и перейти к разделу \\\"How to configure external runners\\\"\",\n \"notSet\": \"Не задано\",\n \"configSaved\": \"Конфигурация ACP сохранена\",\n \"configFailed\": \"Не удалось сохранить конфигурацию ACP\",\n \"deleteTitle\": \"Удалить {{name}}\",\n \"deleteConfirm\": \"Удалить этот ACP агент? Это действие нельзя отменить.\",\n \"deleteSuccess\": \"ACP агент удалён\",\n \"deleteFailed\": \"Не удалось удалить ACP агент\",\n \"nodeSettings\": \"Настройки Node\",\n \"nodePath\": \"Путь к Node\",\n \"chooseOtherNode\": \"Выбрать другой Node...\",\n \"selectNodePath\": \"Выбрать путь к Node\",\n \"nodePathPrompt\": \"Введите путь к исполняемому файлу node\",\n \"nodeSaved\": \"Настройки Node сохранены\",\n \"nodeLoadFailed\": \"Не удалось загрузить настройки Node\",\n \"nodeSaveFailed\": \"Путь к Node недоступен\",\n \"nodeRuntime\": {\n \"bundled\": \"Встроенный Node\",\n \"system\": \"Системный Node\",\n \"custom\": \"Пользовательский Node\"\n },\n \"nodeRuntimeReason\": {\n \"systemNodeMissing\": \"Системный Node не обнаружен\",\n \"nodeMissing\": \"Путь к Node не существует\",\n \"npxMissing\": \"npx не найден\",\n \"versionCheckFailed\": \"Проверка версии не удалась\",\n \"unavailable\": \"Недоступно\"\n }\n },\n \"backup\": {\n \"title\": \"Резервные копии\",\n \"description\": \"Резервное копирование и восстановление рабочих пространств агентов и конфигураций.\",\n \"create\": \"Создать резервную копию\",\n \"import\": \"Импорт\",\n \"export\": \"Экспорт\",\n \"restore\": \"Восстановить\",\n \"delete\": \"Удалить\",\n \"batchDelete\": \"Удалить выбранные\",\n \"batchDeleteConfirm\": \"Вы уверены, что хотите удалить {{count}} резервную(ых) копию(й)? Это действие нельзя отменить.\",\n \"deleteConfirm\": \"Вы уверены, что хотите удалить эту резервную копию? Это действие нельзя отменить.\",\n \"name\": \"Название\",\n \"createdAt\": \"Создана\",\n \"scopeSummary\": \"Область\",\n \"descriptionLabel\": \"Описание\",\n \"noBackups\": \"Резервных копий пока нет. Создайте одну для сохранения текущей конфигурации.\",\n \"loadFailed\": \"Не удалось загрузить список резервных копий\",\n \"createTitle\": \"Создать резервную копию\",\n \"backupMode\": \"Режим резервного копирования\",\n \"fullBackup\": \"Полная резервная копия\",\n \"fullBackupDesc\": \"Резервное копирование всего, включая все рабочие пространства агентов, глобальные настройки, пул навыков и секреты\",\n \"customBackup\": \"Пользовательская резервная копия\",\n \"partialBackup\": \"Частичная резервная копия\",\n \"partialBackupDesc\": \"Настроить содержимое резервной копии\",\n \"nameRequired\": \"Название резервной копии обязательно\",\n \"namePlaceholder\": \"Введите название резервной копии\",\n \"descriptionPlaceholder\": \"Необязательное описание...\",\n \"scopeAgents\": \"Рабочие пространства агентов\",\n \"scopeGlobalConfig\": \"Глобальные настройки\",\n \"scopeSkillPool\": \"Пул навыков\",\n \"scopeSecrets\": \"Секреты\",\n \"scopeSecretsHint\": \"Включает секреты провайдеров моделей (API-ключи), переменные окружения и другую конфиденциальную информацию\",\n \"securityNotice\": \"Эта резервная копия может содержать конфиденциальные учётные данные. Рабочие пространства агентов включают учётные данные каналов (токены ботов, секреты приложений и т.д.), а секреты провайдеров моделей включают API-ключи. Не передавайте файлы резервных копий другим лицам.\",\n \"searchPlaceholder\": \"Поиск по названию или ID...\",\n \"total\": \"Всего {{count}}\",\n \"agentsPlaceholder\": \"Выберите рабочие пространства агентов (оставьте пустым для всех)\",\n \"selectAll\": \"Выбрать все\",\n \"deselectAll\": \"Снять выбор\",\n \"createSuccess\": \"Резервная копия успешно создана\",\n \"createFailed\": \"Не удалось создать резервную копию\",\n \"restoreTitle\": \"Восстановить резервную копию\",\n \"restoreScope\": \"Область восстановления\",\n \"restoreFullWarning\": \"Полное восстановление полностью заменит всё содержимое текущего экземпляра (всех агентов, глобальную конфигурацию, пул навыков и секреты). Это действие нельзя отменить. Во время восстановления связанные функции будут недоступны. После завершения восстановления перезапустите сервис.\",\n \"agentGroupExisting\": \"Существующие\",\n \"agentGroupNew\": \"Новые\",\n \"agentExists\": \"Существует\",\n \"agentNew\": \"Новый\",\n \"agentActionReplace\": \"Заменить\",\n \"agentActionAdd\": \"Добавить\",\n \"agentColumnName\": \"Агент\",\n \"agentColumnWorkspace\": \"Рабочая область\",\n \"agentTotal\": \"Всего {{count}} агентов\",\n \"noAgentsInBackup\": \"В резервной копии нет рабочих областей агентов\",\n \"loadingAgents\": \"Загрузка списка агентов...\",\n \"agentSearchPlaceholder\": \"Поиск по имени или ID...\",\n \"agentSearchTotal\": \"{{count}} из {{total}} агентов\",\n \"restoreCustomSummary\": \"Выбрано: {{existing}} существующих · {{added}} новых\",\n \"defaultWorkspaceDirUnused\": \"Нет новых агентов\",\n \"otherOptions\": \"Другие параметры\",\n \"defaultWorkspaceDirDefault\": \"<Путь по умолчанию>/{{aid}}\",\n \"restoreWarningModify\": \"Восстановление изменит текущие конфигурации. Это действие нельзя отменить.\",\n \"restoreWarningRestart\": \"Во время восстановления связанные функции будут недоступны. После завершения восстановления перезапустите сервис.\",\n \"preRestoreBackupTitle\": \"Создать резервную копию перед восстановлением\",\n \"preRestoreBackupContent\": \"Операция восстановления необратима. Хотите сначала создать резервную копию текущего состояния? Это позволит откатиться, если во время восстановления возникнут проблемы.\",\n \"preRestoreBackupYes\": \"Да, сначала создать резервную копию\",\n \"preRestoreBackupNo\": \"Нет, восстановить сразу\",\n \"preRestoreBackupDesc\": \"Автоматически созданная резервная копия перед восстановлением\",\n \"preRestoreBackupFailed\": \"Не удалось создать резервную копию перед восстановлением\",\n \"preRestoreBackupSuccess\": \"Резервная копия создана успешно, ID: {{id}}\",\n \"creatingPreRestoreBackup\": \"Создание резервной копии перед восстановлением...\",\n \"skipAndRestore\": \"Пропустить и продолжить восстановление\",\n \"restoreConfirm\": \"Я подтверждаю, что хочу восстановить эту резервную копию\",\n \"restoreMode\": \"Режим восстановления\",\n \"restoreModeFull\": \"Полное восстановление\",\n \"restoreModeFullDesc\": \"Полная замена всего содержимого текущего экземпляра, включая всех агентов, глобальную конфигурацию, пул навыков и секреты\",\n \"restoreModeCustom\": \"Выборочное восстановление\",\n \"restoreModeCustomDesc\": \"Выберите содержимое для восстановления, например, отдельных агентов, конфигурацию и т.д.; существующие агенты, не включённые в это восстановление, сохранятся\",\n \"restoreModeFullDisabled\": \"Только для полной резервной копии\",\n \"defaultWorkspaceDir\": \"Каталог рабочей области агента по умолчанию\",\n \"defaultWorkspaceDirPlaceholder\": \"Оставьте пустым для использования ~/.qwenpaw/workspaces\",\n \"defaultWorkspaceDirHint\": \"Если исходный путь рабочей области агента не существует, агент будет сохранён по пути <путь по умолчанию>/<ID агента>. Если путь существует, используется исходный путь агента.\",\n \"restoreStrategy\": \"Стратегия восстановления\",\n \"restoreStrategyPreserve\": \"Сохранить локальные настройки безопасности и MCP\",\n \"restoreStrategyPreserveDesc\": \"Сохранить защитные настройки безопасности и конфигурацию MCP этого экземпляра.\",\n \"restoreStrategyRestore\": \"Восстановить эти настройки из резервной копии\",\n \"restoreStrategyRestoreDesc\": \"Использовать настройки безопасности и MCP из резервной копии.\",\n \"restoreSuccess\": \"Резервная копия успешно восстановлена. Пожалуйста, перезапустите сервис.\",\n \"restoreSuccessPreserved\": \"Резервная копия успешно восстановлена. Сохранены локальные настройки: {{keys}}. Пожалуйста, перезапустите сервис.\",\n \"restoreSuccessWithBackup\": \"Резервная копия успешно восстановлена. ID резервной копии до восстановления: {{preRestoreId}}. Пожалуйста, перезапустите сервис.\",\n \"restoreFailed\": \"Не удалось восстановить резервную копию\",\n \"restoreTargetBusy\": \"Не удалось восстановить резервную копию: эти каталоги все еще используются. Закройте браузер или процесс, который их использует, и повторите попытку. Если каталоги остаются заблокированными, перезапустите систему и попробуйте снова.\",\n \"detailLoadFailed\": \"Не удалось загрузить детали резервной копии; восстановление недоступно\",\n \"trustLocalBanner\": \"Локальная резервная копия — по умолчанию полное восстановление\",\n \"trustForeignBanner\": \"Импортированная резервная копия — локальные настройки безопасности и MCP сохраняются по умолчанию\",\n \"trustLegacyBanner\": \"Устаревшая резервная копия — перед восстановлением нужно подтвердить доверие\",\n \"trustForeignTitle\": \"Доверять этой резервной копии?\",\n \"trustLegacyTitle\": \"Доверять устаревшей резервной копии?\",\n \"unknownBackupName\": \"Архив резервной копии\",\n \"trustForeignDesc\": \"Эта резервная копия не подписана данным экземпляром. Продолжайте только если доверяете источнику; при восстановлении локальные настройки безопасности и MCP будут сохранены по умолчанию.\",\n \"trustLegacyDesc\": \"У этой старой резервной копии нет локальной подписи. Продолжайте только если доверяете источнику; этот экземпляр подпишет ее перед восстановлением.\",\n \"deleteSuccess\": \"Резервная(ые) копия(и) успешно удалена(ы)\",\n \"deleteFailed\": \"Не удалось удалить резервную(ые) копию(и)\",\n \"exportFailed\": \"Не удалось экспортировать резервную копию\",\n \"exportWarningTitle\": \"Предупреждение о конфиденциальной информации\",\n \"exportWarningContent\": \"Эта резервная копия может содержать конфиденциальную информацию учетных данных. Рабочие пространства агентов содержат учетные данные каналов (такие как токены ботов, секреты приложений и т.д.), а секреты поставщиков моделей содержат API-ключи. Не передавайте файл резервной копии другим лицам.\",\n \"exportConfirm\": \"Подтвердить экспорт\",\n \"importTitle\": \"Импорт резервной копии\",\n \"importSuccess\": \"Резервная копия успешно импортирована\",\n \"importFailed\": \"Не удалось импортировать резервную копию\",\n \"importConflictTitle\": \"Резервная копия уже существует\",\n \"importConflictDesc\": \"Резервная копия с таким же ID уже существует. Хотите заменить её?\",\n \"importReplace\": \"Заменить\",\n \"agents\": \"{{count}} агент(ов)\",\n \"allAgents\": \"Все агенты\",\n \"globalConfig\": \"Глобальные настройки\",\n \"skillPool\": \"Пул навыков\",\n \"secrets\": \"Секреты\",\n \"selected\": \"Выбрано {{count}}\",\n \"fileCount\": \"{{count}} файлов\",\n \"totalSize\": \"{{size}}\",\n \"progressStarting\": \"Инициализация резервной копии...\",\n \"progressAgent\": \"Резервное копирование агента {{index}} / {{total}}...\",\n \"progressSaving\": \"Сохранение файла резервной копии...\",\n \"progressDone\": \"Резервная копия создана\",\n \"localModelsNotice\": \"Файлы резервной копии не включают локальные файлы моделей. При миграции между устройствами необходимо повторно загрузить нужные локальные модели на целевом устройстве.\"\n },\n \"approval\": {\n \"title\": \"Требуется подтверждение безопасности\",\n \"tool\": \"Инструмент\",\n \"severity\": \"Уровень угрозы\",\n \"findings\": \"Находки\",\n \"parameters\": \"Параметры\",\n \"approve\": \"Подтвердить\",\n \"deny\": \"Отклонить\",\n \"source\": \"Источник\",\n \"subSession\": \"Подагент\",\n \"approved\": \"Вызов инструмента подтвержден\",\n \"denied\": \"Вызов инструмента отклонен\",\n \"approveFailed\": \"Не удалось подтвердить\",\n \"denyFailed\": \"Не удалось отклонить\",\n \"approvePattern\": \"Всегда Разрешить\",\n \"strictModeHint\": \"Всегда разрешить недоступно в строгом режиме\",\n \"approveExact\": \"Только Сейчас\",\n \"approvalScope\": \"Область подтверждения\",\n \"details\": \"Подробности\",\n \"timeoutAutoDenied\": \"Время истекло, автоматически отклонено\",\n \"acknowledge\": \"Понятно\",\n \"builtinSource\": \"Встроенный\",\n \"ownerAgent\": \"Владелец агента\",\n \"executingAgent\": \"Исполняющий агент\"\n },\n \"pluginManager\": {\n \"title\": \"Менеджер плагинов\",\n \"description\": \"Установка, управление и удаление плагинов без перезапуска службы.\",\n \"installed\": \"Установленные плагины\",\n \"noPlugins\": \"Плагины еще не установлены\",\n \"installBtn\": \"Установить плагин\",\n \"installTitle\": \"Установка плагина\",\n \"dropPrimary\": \"Перетащите папку или ZIP-файл сюда\",\n \"dropSecondary\": \"или нажмите, чтобы выбрать ZIP-файл\",\n \"orFromUrl\": \"или из URL\",\n \"installFromUrl\": \"Установить из URL\",\n \"urlPlaceholder\": \"https://example.com/plugin.zip\",\n \"zipOnly\": \"При перетаскивании файлов поддерживаются только ZIP-файлы — перетащите папку для установки из директории\",\n \"dropFailed\": \"Не удалось прочитать перетащенную папку\",\n \"restartHint\": \"Некоторые плагины (например, хуки, monkey-patches) могут требовать перезапуска приложения для полного вступления в силу или удаления.\",\n \"installing\": \"Установка...\",\n \"installSuccess\": \"Плагин успешно установлен\",\n \"installFailed\": \"Не удалось установить плагин\",\n \"uninstall\": \"Удалить\",\n \"uninstallConfirm\": \"Удалить плагин \\\"{{name}}\\\"? Это действие нельзя отменить. Некоторые плагины могут требовать перезапуска приложения для полного удаления.\",\n \"uninstalling\": \"Удаление...\",\n \"uninstallSuccess\": \"Плагин успешно удалён\",\n \"uninstallFailed\": \"Не удалось удалить плагин\",\n \"statusLoaded\": \"Работает\",\n \"statusUnloaded\": \"Не загружен\",\n \"type\": \"Тип\",\n \"version\": \"Версия\",\n \"author\": \"Автор\",\n \"unknown\": \"Неизвестно\",\n \"loadFailed\": \"Не удалось загрузить список плагинов\",\n \"confirmTitle\": \"Подтвердить удаление\",\n \"officialTitle\": \"Официальные плагины\",\n \"officialDesc\": \"Установка опубликованных плагинов с CDN загрузок.\",\n \"catalogRefresh\": \"Обновить\",\n \"catalogEmpty\": \"Нет доступных официальных плагинов (каталог CDN может быть пуст)\",\n \"catalogLoadFailed\": \"Не удалось загрузить каталог официальных плагинов\",\n \"catalogInstall\": \"Установить\",\n \"catalogReinstall\": \"Переустановить\",\n \"catalogUpgrade\": \"Доступно обновление\",\n \"catalogUpgradeBtn\": \"Обновить\",\n \"catalogInstalled\": \"Установлен\",\n \"catalogSha256\": \"SHA256\",\n \"kindBundle\": \"Пакет\",\n \"kindTool\": \"Инструмент\",\n \"filterByName\": \"Фильтр по имени\",\n \"filterByKind\": \"Фильтр по типу\",\n \"marketTitle\": \"Plugin Market\",\n \"marketEmpty\": \"No plugins found in market\",\n \"marketLoadFailed\": \"Failed to load plugin market\",\n \"marketUnavailable\": \"Plugin Market is currently under maintenance, please try again later\",\n \"marketAll\": \"All\",\n \"marketSearch\": \"Search plugins...\",\n \"marketCategory\": \"Filter by category\",\n \"marketSearchResult\": \"Found {{count}} plugins matching \\\"{{keyword}}\\\"\",\n \"marketDetails\": \"Details\",\n \"marketDownloads\": \"Downloads\",\n \"marketDeveloper\": \"Developer\",\n \"marketSortLabel\": \"Sort plugins\",\n \"marketSortDownloads\": \"Most downloaded\",\n \"marketSortUpdated\": \"Recently updated\",\n \"marketSortFavorites\": \"Most favorited\"\n },\n \"inbox\": {\n \"title\": \"Ежедневный AI-сбор\",\n \"tabApprovals\": \"Подтверждения\",\n \"tabPushMessages\": \"Push-сообщения\",\n \"tabHarvests\": \"AI-сбор\",\n \"summaryApprovals\": \"Подтверждения\",\n \"summaryPushUnread\": \"Непрочитанные push\",\n \"summaryHarvests\": \"Сборы\",\n \"from\": \"От:\",\n \"requestedBy\": \"Кем:\",\n \"markRead\": \"Прочитано\",\n \"markAllRead\": \"Прочитать всё\",\n \"markAllReadNoUnread\": \"Нет непрочитанных сообщений\",\n \"markAllReadSuccess\": \"Отмечено как прочитанное: {{count}} сообщ.\",\n \"selectAllCurrentPage\": \"Выбрать текущую страницу\",\n \"selectedItems\": \"Выбрано: {{count}}\",\n \"batchDeleteButton\": \"Удалить выбранные\",\n \"batchOperation\": \"Массовые действия\",\n \"exitBatch\": \"Выйти\",\n \"batchDeleteConfirm\": \"Удалить {{count}} выбранных push-сообщений?\",\n \"batchDeleteSuccess\": \"Удалено push-сообщений: {{count}}\",\n \"pushCronHeader\": \"Cron-задача: {{name}}\",\n \"viewDetails\": \"Подробнее\",\n \"deleteMessageConfirm\": \"Удалить это push-сообщение?\",\n \"viewAll\": \"Показать все\",\n \"read\": \"Читать\",\n \"approve\": \"Подтвердить\",\n \"reject\": \"Отклонить\",\n \"ready\": \"Готово\",\n \"harvestNow\": \"Собрать сейчас\",\n \"createHarvest\": \"Создать сбор\",\n \"createFirstHarvest\": \"Создайте первый сбор\",\n \"createSuccess\": \"Сбор успешно создан\",\n \"emptyApprovals\": \"Нет ожидающих подтверждений\",\n \"emptyPush\": \"Нет push-сообщений\",\n \"wobbleEnable\": \"Включить уведомление о подтверждении\",\n \"wobbleDisable\": \"Отключить уведомление о подтверждении\",\n \"emptyHarvests\": \"Сборов пока нет\",\n \"statusReadyToHarvest\": \"Готово к сбору\",\n \"statusGrowing\": \"Растёт\",\n \"harvestedTimes\": \"Собрано раз: {{count}}\",\n \"harvestSuccessRate\": \"Успешность {{rate}}%\",\n \"messageDetailComingSoon\": \"Подробный вид сообщения — скоро!\",\n \"harvestSettingsComingSoon\": \"Настройки сбора — скоро!\",\n \"messageDetailTitle\": \"Детали выполнения\",\n \"detailCronTitle\": \"Cron-задача: {{name}}\",\n \"detailHeartbeatTitle\": \"Пульс\",\n \"messageNotFound\": \"Сообщение не найдено\",\n \"detailTaskName\": \"Имя задачи\",\n \"detailTaskId\": \"ID задачи\",\n \"detailStatus\": \"Статус\",\n \"detailSeverity\": \"Важность\",\n \"detailAgent\": \"Агент\",\n \"detailSource\": \"Источник\",\n \"detailSourceCronJob\": \"Cron-задача\",\n \"detailSourceHeartbeat\": \"Пульс\",\n \"detailTrigger\": \"Триггер\",\n \"detailDuration\": \"Длительность\",\n \"detailExecutedAt\": \"Выполнено в\",\n \"detailReceivedAt\": \"Получено в\",\n \"detailRunId\": \"ID запуска\",\n \"detailContent\": \"Вывод\",\n \"detailPayload\": \"Данные\",\n \"detailExecutionTrace\": \"Трассировка выполнения\",\n \"detailTraceEmpty\": \"Трассировка выполнения недоступна\",\n \"filterByAgent\": \"Фильтр по агенту\",\n \"filterBySourceType\": \"Фильтр по источнику\",\n \"sourceTypeCron\": \"Cron\",\n \"sourceTypeHeartbeat\": \"Heartbeat\",\n \"sourceTypeMemory\": \"Memory\"\n },\n \"codingMode\": {\n \"title\": \"Режим кодирования\",\n \"experimental\": \"Экспериментальная функция\",\n \"experimentalDesc\": \"Режим кодирования превращает QwenPaw в лёгкую Web IDE с редактором кода, деревом файлов, интеграцией Git и AI-помощником для программирования.\",\n \"experimentalNote\": \"Эта функция является экспериментальной и находится в активной разработке. Некоторые возможности могут быть ограничены или изменены в будущих обновлениях.\",\n \"confirmBtn\": \"Включить режим кодирования\",\n \"enterTooltip\": \"Войти в режим кодирования\",\n \"exitTooltip\": \"Выйти из режима кодирования\",\n \"btnCode\": \"Код\",\n \"btnChat\": \"Чат\",\n \"selectProject\": \"Выбрать проект для работы\",\n \"selectProjectDesc\": \"Выберите проект или используйте рабочее пространство по умолчанию\",\n \"tabWorkspace\": \"Рабочее пространство по умолчанию\",\n \"tabClone\": \"Клонировать репозиторий\",\n \"tabLocal\": \"Импортировать локальный проект\",\n \"tabNew\": \"Новый проект\",\n \"workspaceDesc\": \"Использовать директорию рабочего пространства текущего агента\",\n \"cloneUrl\": \"URL Git-репозитория\",\n \"cloneUrlPlaceholder\": \"https://github.com/user/repo.git\",\n \"cloneName\": \"Название проекта (необязательно)\",\n \"cloneNamePlaceholder\": \"Оставьте пустым для определения из URL\",\n \"cloneBtn\": \"Начать клонирование\",\n \"cloning\": \"Клонирование...\",\n \"localPath\": \"Путь к локальной директории\",\n \"localPathPlaceholder\": \"/Users/ваше-имя/мой-проект\",\n \"localPathHint\": \"Введите абсолютный путь к вашему локальному проекту\",\n \"openBtn\": \"Импортировать\",\n \"newName\": \"Название проекта\",\n \"newNamePlaceholder\": \"мой-проект\",\n \"createBtn\": \"Создать проект\",\n \"switchProject\": \"Сменить проект\",\n \"currentProject\": \"Текущий проект\",\n \"resetToWorkspace\": \"Сбросить до рабочего пространства по умолчанию\",\n \"dropPrimary\": \"Перетащите папку сюда или нажмите для выбора\",\n \"dropSecondary\": \"Перетащите из Finder или вставьте путь ниже\",\n \"pathHint\": \"Введите полный абсолютный путь (начинающийся с / или ~)\",\n \"importing\": \"Импорт...\",\n \"dropFailed\": \"Не удалось прочитать папку\",\n \"workingDir\": \"Рабочая директория\",\n \"importCopyNote\": \"Папка будет скопирована в coding_projects/. Оригинальные файлы не будут изменены.\",\n \"importCopyDesc\": \"Импорт создаст копию папки. Агент работает с копией, оригинальные файлы не изменяются. Чтобы работать с оригиналом напрямую, используйте «Открыть каталог».\",\n \"tabOpenDir\": \"Открыть каталог\",\n \"openDirDesc\": \"Открыть существующий локальный каталог — файлы не копируются\",\n \"openDirBtn\": \"Открыть этот каталог\",\n \"openDirLoading\": \"Загрузка...\",\n \"openDirEmpty\": \"Нет подкаталогов\",\n \"openDirHome\": \"Домой\",\n \"openDirRefresh\": \"Обновить\",\n \"openDirHiddenFolders\": \"Скрытые папки\"\n },\n \"market\": {\n \"searchPlaceholder\": \"Поиск навыков по платформам\",\n \"categoryAll\": \"Все\",\n \"categoryPlaceholder\": \"Поиск категории\",\n \"searchResult\": \"Найдено навыков по запросу \\\"{{keyword}}\\\": {{count}}\",\n \"providerUnavailable\": \"Источник недоступен\",\n \"noResults\": \"Ничего не найдено\",\n \"startTyping\": \"Введите запрос выше, чтобы найти навык в маркете\",\n \"browseHint\": \"Выберите категорию или введите ключевое слово, чтобы просмотреть навыки в {{providers}}\",\n \"noDescription\": \"Описание отсутствует\",\n \"browseMarket\": \"Обзор маркета\",\n \"installQueue\": \"Очередь установки\",\n \"clearCompleted\": \"Очистить\",\n \"retry\": \"Повторить\",\n \"status\": {\n \"queued\": \"В очереди\",\n \"installing\": \"Установка\",\n \"completed\": \"Готово\",\n \"failed\": \"Ошибка\",\n \"cancelled\": \"Отменено\"\n },\n \"loadMore\": \"Загрузить ещё\",\n \"noMoreResults\": \"Больше результатов нет\",\n \"detail\": {\n \"title\": \"Сведения о навыке\",\n \"version\": \"Версия\",\n \"author\": \"Автор\",\n \"sourceUrl\": \"URL установки\",\n \"slug\": \"Идентификатор\",\n \"missing\": \"—\"\n },\n \"stats\": {\n \"downloads\": \"Скачивания\",\n \"stars\": \"Звёзды\",\n \"installs\": \"Установки\",\n \"likes\": \"Лайки\",\n \"views\": \"Просмотры\",\n \"category\": \"Категория\",\n \"updatedAt\": \"Обновлено\"\n },\n \"queueMsg\": {\n \"timedOut\": \"Время установки истекло\",\n \"failedPrefix\": \"Ошибка: {{msg}}\"\n }\n },\n \"startup\": {\n \"starting\": \"Запуск сервера...\",\n \"checking\": \"Подключение к серверу...\",\n \"error\": \"Не удалось запустить сервер.\",\n \"errorHint\": \"Не удалось запустить процесс сервера. Проверьте журналы приложения.\",\n \"errorDetails\": \"Показать сведения об ошибке\",\n \"timeout\": \"Не удалось запустить сервер за {{seconds}} секунд.\",\n \"timeoutHint\": \"Не удалось запустить бэкенд. Повторите попытку или проверьте журналы приложения.\",\n \"retry\": \"Повторить\"\n },\n \"preview\": {\n \"error\": {\n \"SENSITIVE_FILE_BLOCKED\": \"Этот файл заблокирован защитой файлов (защита конфиденциальных файлов).\",\n \"OUTSIDE_WORKSPACE\": \"Этот файл находится за пределами рабочего пространства. Включите «Разрешить предпросмотр файлов вне рабочего пространства» в Настройки → Безопасность → Защита файлов.\",\n \"FORBIDDEN\": \"Доступ запрещён: недостаточно прав для предпросмотра этого файла.\",\n \"NOT_FOUND\": \"Файл не найден. Возможно, он был перемещён или удалён. Если использован относительный путь, попробуйте абсолютный.\",\n \"LOAD_FAILED\": \"Не удалось загрузить предпросмотр файла.\",\n \"LOAD_FAILED_DETAIL\": \"Не удалось загрузить предпросмотр файла: {{detail}}\"\n }\n },\n \"tool\": {\n \"readFile\": \"Читать {{file}}\",\n \"readFileDefault\": \"Читать файл\",\n \"writeFile\": \"Создать {{file}}\",\n \"writeFileDefault\": \"Создать файл\",\n \"editFile\": \"Редактировать {{file}}\",\n \"editFileDefault\": \"Редактировать файл\",\n \"appendFile\": \"Добавить в {{file}}\",\n \"appendFileDefault\": \"Добавить в файл\",\n \"grepSearch\": \"Grep {{pattern}}\",\n \"grepSearchDefault\": \"Grep поиск\",\n \"globSearch\": \"Glob {{pattern}}\",\n \"globSearchDefault\": \"Glob поиск\",\n \"viewImage\": \"Просмотр изображения {{file}}\",\n \"viewImageDefault\": \"Просмотр изображения\",\n \"viewVideo\": \"Просмотр видео {{file}}\",\n \"viewVideoDefault\": \"Просмотр видео\",\n \"desktopScreenshot\": \"Снимок рабочего стола\",\n \"sendFile\": \"Отправить {{file}}\",\n \"sendFileDefault\": \"Отправить файл\",\n \"browserUse\": \"Просмотр {{detail}}\",\n \"browserNavigate\": \"Открыть {{url}}\",\n \"browserNavigateDefault\": \"Открыть страницу\",\n \"browserClick\": \"Нажать\",\n \"browserType\": \"Ввести текст\",\n \"browserSnapshot\": \"Снимок страницы\",\n \"browserScroll\": \"Прокрутка\",\n \"getCurrentTime\": \"Получить текущее время\",\n \"setTimezone\": \"Установить {{timezone}}\",\n \"getTokenUsage\": \"Запросить использование токенов\",\n \"memorySearch\": \"Поиск в памяти {{query}}\",\n \"memorySearchDefault\": \"Поиск в памяти\",\n \"listAgents\": \"Список агентов\",\n \"chatWithAgent\": \"Чат с {{agent}}\",\n \"chatWithAgentDefault\": \"Чат с агентом\",\n \"submitToAgent\": \"Отправить {{agent}}{{task}}\",\n \"submitToAgentDefault\": \"Отправить задачу\",\n \"checkAgentTask\": \"Проверить {{agent}} #{{taskId}}\",\n \"checkAgentTaskAgent\": \"Проверить {{agent}}\",\n \"checkAgentTaskDefault\": \"Проверить задачу\",\n \"delegateExternalAgent\": \"Делегировать {{runner}}\",\n \"delegateExternalAgentDefault\": \"Делегировать внешнему агенту\",\n \"materializeSkill\": \"Создать {{skill}}\",\n \"materializeSkillDefault\": \"Создать навык\",\n \"shell\": \"Выполнить {{command}}\",\n \"shellDefault\": \"Выполнить команду\",\n \"formatTable\": {\n \"file\": \"Файл\",\n \"lineNumber\": \"Строка\",\n \"score\": \"Оценка\",\n \"summary\": \"Сводка\",\n \"name\": \"Имя\",\n \"id\": \"ID\",\n \"description\": \"Описание\",\n \"status\": \"Статус\"\n },\n \"loading\": \"Загрузка...\",\n \"inputProgress\": \"Получено символов: {{count}}\",\n \"rawInputPreview\": \"Необработанный ввод в реальном времени (последние 4 096 символов)\",\n \"browserAction\": {\n \"startHeaded\": \"Запустить браузер (с интерфейсом)\",\n \"start\": \"Запустить браузер\",\n \"stop\": \"Остановить браузер\",\n \"open\": \"Открыть {{url}}\",\n \"openDefault\": \"Открыть страницу\",\n \"navigate\": \"Перейти на {{url}}\",\n \"navigateDefault\": \"Навигация\",\n \"navigateBack\": \"Назад\",\n \"click\": \"Нажать {{selector}}\",\n \"clickDefault\": \"Нажать элемент\",\n \"type\": \"Ввести {{text}}\",\n \"typeDefault\": \"Ввести текст\",\n \"snapshot\": \"Получить снимок\",\n \"screenshot\": \"Снимок экрана {{path}}\",\n \"screenshotDefault\": \"Снимок экрана\",\n \"eval\": \"Выполнить {{code}}\",\n \"evalDefault\": \"Выполнить код\",\n \"runCode\": \"Запустить {{code}}\",\n \"runCodeDefault\": \"Запустить код\",\n \"closePage\": \"Закрыть страницу\",\n \"tabs\": \"Вкладки {{action}}\",\n \"tabsDefault\": \"Управление вкладками\",\n \"fillForm\": \"Заполнить форму\",\n \"fileUpload\": \"Загрузить {{filename}}\",\n \"fileUploadDefault\": \"Загрузить файл\",\n \"fileDownload\": \"Скачать {{target}}\",\n \"fileDownloadDefault\": \"Скачать файл\",\n \"pressKey\": \"Нажать {{key}}\",\n \"pressKeyDefault\": \"Нажать клавишу\",\n \"hover\": \"Навести на {{selector}}\",\n \"hoverDefault\": \"Навести на элемент\",\n \"drag\": \"Перетащить\",\n \"selectOption\": \"Выбрать опцию\",\n \"waitFor\": \"Ждать {{target}}\",\n \"waitForDefault\": \"Ждать элемент\",\n \"resize\": \"Изменить размер {{w}}x{{h}}\",\n \"resizeDefault\": \"Изменить размер окна\",\n \"pdf\": \"Экспорт PDF {{path}}\",\n \"pdfDefault\": \"Экспорт PDF\",\n \"install\": \"Установить\",\n \"batch\": \"Пакет\"\n },\n \"lineBadge\": {\n \"lines\": \"{{count}} строк\",\n \"addLines\": \"+{{count}} строк\",\n \"delLines\": \"-{{count}} строк\",\n \"matches\": \"{{count}} совпадений\",\n \"files\": \"{{count}} файлов\"\n },\n \"inlineResult\": {\n \"taskId\": \"Задача #{{id}}\",\n \"submitted\": \"Отправлено\"\n },\n \"execute\": \"Запустить {{tool}}\",\n \"executeDefault\": \"Запустить инструмент\"\n },\n \"loop\": {\n \"gotoSettings\": \"Настройки Loop агента\",\n \"selectorTitle\": \"Режим Loop\",\n \"selectorHint\": \"Выберите способ продолжения работы агента\",\n \"selectorAria\": \"Выбрать режим Loop\",\n \"builtInModes\": \"Встроенные\",\n \"customModes\": \"Пользовательские и плагины\",\n \"starting\": \"Запуск\",\n \"running\": \"Выполняется\",\n \"awaiting_user\": \"Ожидание вашего ответа\",\n \"activePersistentDescription\": \"Режим действует в этом диалоге до завершения задачи или сброса диалога.\",\n \"activeCustomDescription\": \"Пользовательский режим действует до команды /mode off, /clear или /new.\",\n \"loadError\": \"Не удалось загрузить некоторые режимы Loop.\",\n \"retry\": \"Повторить\",\n \"modes\": {\n \"default\": {\n \"name\": \"По умолчанию\",\n \"description\": \"Стандартный управляемый цикл агента.\"\n },\n \"goal\": {\n \"name\": \"Цель\",\n \"description\": \"Последовательная работа над конкретной проверяемой целью.\"\n },\n \"mission\": {\n \"name\": \"Миссия\",\n \"description\": \"Структурированная длительная многоэтапная задача.\"\n }\n }\n }\n}\n"}, "db8fb6391097791b08f63623ba4cd88421ec5cdb": {"t": 1785846849.674076, "n": 187835, "h": "276b96f0e3c74de5", "c": "{\n \"common\": {\n \"save\": \"Сохранить\",\n \"reset\": \"Сбросить\",\n \"cancel\": \"Отмена\",\n \"confirm\": \"Подтвердить\",\n \"delete\": \"Удалить\",\n \"edit\": \"Редактировать\",\n \"create\": \"Создать\",\n \"upload\": \"Загрузить\",\n \"download\": \"Скачать\",\n \"refresh\": \"Обновить\",\n \"search\": \"Поиск\",\n \"enable\": \"Включить\",\n \"disable\": \"Отключить\",\n \"enabled\": \"Включено\",\n \"disabled\": \"Отключено\",\n \"preview\": \"Предпросмотр\",\n \"view\": \"Просмотр\",\n \"content\": \"Содержимое\",\n \"loading\": \"Загрузка...\",\n \"saving\": \"Сохранение...\",\n \"copy\": \"Копировать\",\n \"copied\": \"Скопировано в буфер обмена\",\n \"copyFailed\": \"Не удалось скопировать в буфер обмена\",\n \"contentPlaceholder\": \"Введите содержимое...\",\n \"help\": \"Справка\",\n \"close\": \"Закрыть\",\n \"actions\": \"Действия\",\n \"total\": \"Всего {{count}}\",\n \"clear\": \"Очистить\",\n \"paste\": \"Вставить\"\n },\n \"harnesses\": {\n \"connected\": \"Подключено\",\n \"disconnected\": \"Отключено\",\n \"notConnected\": \"Не подключено\",\n \"comingSoon\": \"Скоро\",\n \"connect\": \"Подключить через ChatGPT\",\n \"disconnect\": \"Отключить\",\n \"apiKeyAuthenticated\": \"Аутентификация по API-ключу\",\n \"chatGptAuthenticated\": \"Подключено к ChatGPT\",\n \"cliAuthenticated\": \"{{type}} CLI аутентифицирован\"\n },\n \"desktop\": {\n \"closeWindow\": {\n \"title\": \"Закрыть окно\",\n \"description\": \"Что сделать при закрытии окна? Выход из приложения остановит все выполняющиеся задачи и запланированные задания.\",\n \"remember\": \"Запомнить мой выбор\",\n \"showWindow\": \"Показать окно\",\n \"minimizeToTray\": \"Свернуть в трей\",\n \"quitApp\": \"Выйти из приложения\",\n \"preference\": \"Закрытие окна\",\n \"askEveryTime\": \"Спрашивать каждый раз\"\n }\n },\n \"chunkError\": {\n \"title\": \"Не удалось загрузить страницу\",\n \"subTitle\": \"Возможно, это вызвано проблемой сети или обновлением приложения.\",\n \"genericTitle\": \"Что-то пошло не так\",\n \"genericSubTitle\": \"При отображении этой страницы произошла непредвиденная ошибка.\",\n \"reload\": \"Перезагрузить\"\n },\n \"nav\": {\n \"chat\": \"Чат\",\n \"control\": \"Управление\",\n \"channels\": \"Каналы\",\n \"sessions\": \"Сессии\",\n \"cronJobs\": \"Cron-задачи\",\n \"heartbeat\": \"Пульс\",\n \"agent\": \"Рабочая область\",\n \"workspace\": \"Файлы\",\n \"skills\": \"Навыки\",\n \"skillPool\": \"Пул навыков\",\n \"tools\": \"Инструменты\",\n \"mcp\": \"MCP\",\n \"agentConfig\": \"Конфигурация\",\n \"settings\": \"Настройки\",\n \"models\": \"Модели\",\n \"environments\": \"Окружения\",\n \"security\": \"Безопасность\",\n \"tokenUsage\": \"Использование токенов\",\n \"agentStats\": \"Статистика агента\",\n \"voiceTranscription\": \"Транскрипция голоса\",\n \"agents\": \"Управление агентами\",\n \"debug\": \"Отладка\",\n \"plugins\": \"Плагины\",\n \"acp\": \"ACP\",\n \"backups\": \"Резервные копии\",\n \"pluginManager\": \"Менеджер плагинов\"\n },\n \"debug\": {\n \"title\": \"Отладка\",\n \"desc\": \"Просмотр файла логов backend-демона для диагностики. Пока открыта эта страница, логи обновляются автоматически.\",\n \"level\": {\n \"all\": \"Все\"\n },\n \"backend\": {\n \"title\": \"Backend-логи\",\n \"autoRefresh\": \"Автообновление\",\n \"newestFirst\": \"Сначала новые\",\n \"updatedAt\": \"Обновлено\",\n \"path\": \"Файл лога\",\n \"notFound\": \"Файл backend-логов пока не найден.\",\n \"placeholder\": \"Здесь появится вывод backend-логов.\",\n \"loadFailed\": \"Не удалось загрузить backend-логи\",\n \"searchPlaceholder\": \"Поиск по backend-логам...\"\n },\n \"actions\": {\n \"refreshBackend\": \"Обновить backend-логи\",\n \"refreshSuccess\": \"Логи обновлены\",\n \"copyBackend\": \"Копировать backend-логи\"\n }\n },\n \"agent\": {\n \"parent\": \"Настройки\",\n \"agents\": \"Агенты\",\n \"management\": \"Управление агентами\",\n \"pageDescription\": \"Создавайте, настраивайте и управляйте несколькими AI-агентами с пользовательскими рабочими пространствами и идентичностями.\",\n \"name\": \"Название\",\n \"id\": \"ID\",\n \"description\": \"Описание\",\n \"workspace\": \"Путь к рабочему пространству\",\n \"backend\": {\n \"column\": \"Бэкенд\",\n \"eyebrow\": \"Архитектура агента\",\n \"typeTitle\": \"Выберите, как работает этот агент\",\n \"typeDescription\": \"Тип агента определяет работу всех диалогов в Chat, Coding и Channel.\",\n \"nativeTitle\": \"Нативный агент QwenPaw\",\n \"nativeBadge\": \"Нативный\",\n \"nativeDescription\": \"Использует настроенные LLM, Skills, инструменты, память и среду выполнения QwenPaw.\",\n \"thirdPartyTitle\": \"Сторонний агент\",\n \"thirdPartyBadge\": \"Сторонний\",\n \"thirdPartyDescription\": \"Использует авторизованную внешнюю среду агента вместо модели QwenPaw.\",\n \"providerTitle\": \"Выберите стороннего агента\",\n \"providerDescription\": \"Авторизация общая для этого устройства; проекты и диалоги изолированы для каждого агента.\",\n \"codexHint\": \"ChatGPT OAuth · Рабочая область агента\",\n \"qoderHint\": \"Учётная запись Qoder или PAT · Рабочая область агента\",\n \"account\": \"Учётная запись Codex\",\n \"qoderAccount\": \"Учётная запись Qoder\",\n \"binary\": \"Исполняемый файл Codex\",\n \"binaryHelp\": \"Оставьте поле пустым, чтобы использовать Codex CLI из Python SDK или найти отдельную установку. При ручной настройке укажите полный путь к codex в macOS/Linux или codex.exe в Windows. Встроенные бинарные файлы приложения ChatGPT и расширений редактора не поддерживаются. QwenPaw также читает CODEX_BINARY и PATH бэкенда.\",\n \"binaryPlaceholder\": \"Автоопределение или путь к исполняемому файлу Codex\",\n \"qoderBinary\": \"Исполняемый файл Qoder\",\n \"qoderBinaryHelp\": \"Оставьте поле пустым, чтобы использовать qodercli из Python SDK или найти установленную версию. При ручной настройке укажите полный путь к qodercli в macOS/Linux или qodercli.exe в Windows. QwenPaw также читает QODERCLI_PATH и PATH бэкенда.\",\n \"qoderBinaryPlaceholder\": \"Автоопределение или путь к qodercli\",\n \"detect\": \"Проверить\",\n \"detectedBinary\": \"Codex обнаружен\",\n \"qoderDetectedBinary\": \"Qoder обнаружен\",\n \"apiKeyLoginHint\": \"Также можно выполнить аутентификацию в терминале: codex login --with-api-key\",\n \"qoderAuthHint\": \"Подключение откроет вход Qoder в браузере. Также можно выполнить qodercli login или задать QODER_PERSONAL_ACCESS_TOKEN перед запуском бэкенда.\",\n \"qoderConnect\": \"Подключить Qoder\",\n \"model\": \"Модель агента\",\n \"modelDefault\": \"Использовать модель агента по умолчанию\",\n \"defaultBadge\": \"По умолчанию\",\n \"reasoningEffort\": \"Уровень рассуждения\",\n \"reasoningDefault\": \"Использовать значение модели по умолчанию\",\n \"codexNotFound\": \"Среда выполнения Codex не найдена. Установите qwenpaw[codex] или укажите отдельный Codex CLI.\",\n \"qoderNotFound\": \"Среда выполнения Qoder не найдена. Установите qwenpaw[qoder] или укажите qodercli.\",\n \"chatSettingsHint\": \"Выберите модель, уровень рассуждения и разрешения на выполнение на панели Chat.\",\n \"capabilityTitle\": \"Возможности QwenPaw\",\n \"inheritedSkills\": \"Навыки\",\n \"inheritedMcp\": \"Серверы MCP\",\n \"runtimeInherited\": \"Наследуются при запуске · новые сеансы\",\n \"notSupported\": \"Не поддерживается провайдером\",\n \"policyCompatibility\": \"Политика MCP\",\n \"toolAllowlistSupported\": \"Список разрешённых инструментов\",\n \"policyLimited\": \"Действуют ограничения провайдера\",\n \"chatModelHint\": \"Настройки агента · применяются со следующего хода\",\n \"appliesNextTurn\": \"Применяется со следующего хода\",\n \"approvalMode\": \"Разрешения на выполнение\",\n \"approvalPresets\": {\n \"ask\": {\n \"name\": \"Спрашивать перед изменениями\",\n \"description\": \"Разрешает изменения рабочей области и запрашивает подтверждение перед действиями с повышенными правами.\"\n },\n \"read-only\": {\n \"name\": \"Только чтение\",\n \"description\": \"Проверяет файлы без изменений.\"\n },\n \"workspace\": {\n \"name\": \"Доступ к рабочей области\",\n \"description\": \"Разрешает изменения рабочей области без подтверждения.\"\n },\n \"accept-edits\": {\n \"name\": \"Разрешать правки\",\n \"description\": \"Разрешает правки файлов, сохраняя остальные проверки.\"\n },\n \"plan\": {\n \"name\": \"Только план\",\n \"description\": \"Анализирует и планирует без изменения файлов.\"\n },\n \"auto\": {\n \"name\": \"Автоматически\",\n \"description\": \"Qoder сам определяет безопасные действия.\"\n },\n \"full-access\": {\n \"name\": \"Полный доступ\",\n \"description\": \"Разрешает неограниченное локальное выполнение без подтверждения.\"\n }\n }\n },\n \"create\": \"Создать агента\",\n \"createTitle\": \"Создать нового агента\",\n \"createSuccess\": \"Агент успешно создан\",\n \"createFailed\": \"Не удалось создать агента\",\n \"edit\": \"Редактировать\",\n \"editTitle\": \"Редактировать агента - {{name}}\",\n \"updateSuccess\": \"Агент успешно обновлён\",\n \"updateFailed\": \"Не удалось обновить агента\",\n \"delete\": \"Удалить\",\n \"deleteConfirm\": \"Подтвердить удаление агента\",\n \"deleteConfirmDesc\": \"После удаления агент станет недоступен, но файлы рабочего пространства будут сохранены\",\n \"deleteSuccess\": \"Агент успешно удалён\",\n \"deleteFailed\": \"Не удалось удалить агента\",\n \"selectAgent\": \"Выбрать агента\",\n \"defaultDisplayName\": \"Агент по умолчанию\",\n \"currentWorkspace\": \"Текущий агент\",\n \"switchSuccess\": \"Агент успешно переключён\",\n \"switchFailed\": \"Не удалось переключить агента\",\n \"loadFailed\": \"Не удалось загрузить список агентов\",\n \"loadConfigFailed\": \"Не удалось загрузить конфигурацию агента\",\n \"saveFailed\": \"Не удалось сохранить агента\",\n \"idRequired\": \"Пожалуйста, введите ID агента\",\n \"idLabel\": \"ID агента (необязательно)\",\n \"idHelp\": \"Оставьте пустым для автогенерации. Допускаются только буквы, цифры, дефисы и подчёркивания.\",\n \"idPattern\": \"ID может содержать только буквы, цифры, подчёркивания и дефисы\",\n \"idPlaceholder\": \"например: my-agent\",\n \"nameRequired\": \"Пожалуйста, введите название агента\",\n \"namePlaceholder\": \"например: Мой агент\",\n \"descriptionPlaceholder\": \"Кратко опишите назначение этого агента...\",\n \"workspaceHelp\": \"Оставьте пустым для автоматического создания в ~/.qwenpaw/workspaces/<id>\",\n \"dragHandleTooltip\": \"Перетащите, чтобы изменить порядок агентов\",\n \"reorderSuccess\": \"Порядок агентов сохранён\",\n \"reorderFailed\": \"Не удалось сохранить порядок агентов\",\n \"defaultNotEditable\": \"Агент по умолчанию не может быть отредактирован\",\n \"defaultNotDeletable\": \"Агент по умолчанию не может быть удалён\",\n \"disable\": \"Отключить\",\n \"enable\": \"Включить\",\n \"disabled\": \"Отключён\",\n \"disableConfirm\": \"Подтвердить отключение агента\",\n \"disableConfirmDesc\": \"После отключения экземпляр агента не будет запущен, но останется видимым в списке\",\n \"enableConfirm\": \"Подтвердить включение агента\",\n \"enableConfirmDesc\": \"После включения агент будет доступен для переключения\",\n \"disableSuccess\": \"Агент успешно отключён\",\n \"enableSuccess\": \"Агент успешно включён\",\n \"toggleFailed\": \"Не удалось переключить состояние агента\",\n \"enableAgent\": \"Включить {{name}}\",\n \"disableAgent\": \"Отключить {{name}}\",\n \"disabledAgents\": \"Отключённые ({{count}})\",\n \"pinnedAgents\": \"Закреплённые\",\n \"otherAgents\": \"Другие агенты\",\n \"pinned\": \"Закреплён\",\n \"defaultPinned\": \"Агент по умолчанию всегда закреплён\",\n \"longPressToPin\": \"Удерживайте для закрепления\",\n \"longPressToUnpin\": \"Удерживайте для открепления\",\n \"pinAgent\": \"Закрепить агента\",\n \"unpinAgent\": \"Открепить агента\",\n \"pinSuccess\": \"Агент закреплён\",\n \"unpinSuccess\": \"Агент откреплён\",\n \"pinFailed\": \"Не удалось изменить закрепление\",\n \"status\": {\n \"disabled\": \"Отключён\",\n \"pending\": \"Ожидает запуска\",\n \"starting\": \"Запускается\",\n \"running\": \"Работает\",\n \"failed\": \"Ошибка запуска\",\n \"waitUntilStarted\": \"Доступно после завершения запуска\"\n },\n \"defaultNotDisablable\": \"Агент по умолчанию нельзя отключить\",\n \"currentAgentDeleted\": \"Текущий агент был удалён, автоматически переключено на агента по умолчанию\",\n \"currentAgentDisabled\": \"Текущий агент был отключён, автоматически переключено на агента по умолчанию\",\n \"switchedToDefault\": \"Автоматически переключено на агента по умолчанию\",\n \"cannotSwitchToDisabled\": \"Этот агент отключён, пожалуйста, сначала включите его на странице управления\",\n \"model\": \"Модель\",\n \"modelHelp\": \"Назначьте конкретную LLM-модель этому агенту. Оставьте пустым для использования глобальной модели по умолчанию.\",\n \"modelPlaceholder\": \"Глобальная по умолчанию\",\n \"noConfiguredModels\": \"Нет настроенных моделей\",\n \"modelColumn\": \"Модель\",\n \"initialSkills\": \"Начальные навыки\",\n \"initialSkillsHelp\": \"Выберите навыки для добавления из пула. Позже можно добавить или удалить навыки.\",\n \"selectAll\": \"Все\",\n \"selectBuiltin\": \"Встроенные\",\n \"selectNone\": \"Очистить\",\n \"noPoolSkills\": \"В пуле нет доступных навыков\",\n \"addSkillsToAgent\": \"Добавить навыки\",\n \"copyTooltip\": \"Копировать конфигурацию агента\",\n \"copyDefaultTooltip\": \"Копировать, используя агента по умолчанию как шаблон\",\n \"copyTitle\": \"Копировать конфигурацию агента\",\n \"copyNameLabel\": \"Имя нового агента\",\n \"copyOptionsLabel\": \"Что копировать\",\n \"copyOptionAgentJson\": \"agent.json (обязательно)\",\n \"copyOptionAgentJsonHint\": \"Всегда копирует разобранную исходную конфигурацию и сбрасывает ID, имя, workspace и channels. Это не побайтовая копия agent.json на диске.\",\n \"copyOptionMdFiles\": \"AGENTS / SOUL / PROFILE / HEARTBEAT / BOOTSTRAP.md\",\n \"copyOptionSkills\": \"skills/ + manifest\",\n \"copyOptionJobs\": \"jobs.json\",\n \"copyOptionJobsHint\": \"Копирует только определения заданий, без истории. Новый агент начнёт выполнять их по расписанию сразу после создания.\",\n \"copySuccess\": \"Конфигурация агента успешно скопирована\",\n \"copyFailed\": \"Не удалось скопировать конфигурацию агента\"\n },\n \"checkpoints\": {\n \"nav\": \"Контрольные точки\",\n \"title\": \"Контрольные точки состояния\",\n \"auto\": \"Автоматически\",\n \"autoEnabled\": \"Автоматические точки включены\",\n \"autoDisabled\": \"Автоматические точки выключены\",\n \"refresh\": \"Обновить\",\n \"snapshot\": \"Снимок\",\n \"snapshotCreated\": \"Снимок создан\",\n \"graph\": \"Граф\",\n \"checkpoint\": \"Контрольная точка\",\n \"type\": \"Тип\",\n \"commit\": \"Коммит\",\n \"createdAt\": \"Создано\",\n \"session\": \"Сеанс\",\n \"channel\": \"Канал\",\n \"parent\": \"Родитель\",\n \"details\": \"Сведения\",\n \"search\": \"Поиск по запросу, имени, сеансу или SHA\",\n \"allTypes\": \"Все типы\",\n \"allSessions\": \"Все сеансы\",\n \"showingLatest\": \"Показано последних: {{count}}\",\n \"loadFailed\": \"Не удалось загрузить контрольные точки\",\n \"retry\": \"Повторить\",\n \"more\": \"Другие действия\",\n \"noMatches\": \"Нет точек, соответствующих фильтрам.\",\n \"empty\": \"Контрольных точек пока нет. История версий появится здесь.\",\n \"kind\": {\n \"auto\": \"Авто\",\n \"snapshot\": \"Снимок\",\n \"safety\": \"Защитная\",\n \"commit\": \"Коммит\"\n },\n \"summary\": { \"total\": \"точек\" },\n \"snapshotDialog\": {\n \"title\": \"Создать снимок\",\n \"session\": \"Сеанс\",\n \"name\": \"Имя\",\n \"placeholder\": \"Необязательное имя\"\n },\n \"gc\": {\n \"settingsAction\": \"Настройки автоочистки\",\n \"settingsTitle\": \"Настройки автоочистки\",\n \"keepCount\": \"Количество автоматических точек\",\n \"keepDays\": \"Хранение автоматических точек (дни)\",\n \"preRestoreDays\": \"Хранение снимков перед восстановлением (дни)\",\n \"settingsSaved\": \"Настройки автоочистки сохранены\",\n \"thoroughAction\": \"Полное сжатие контрольных точек\",\n \"thoroughTitle\": \"Полностью сжать контрольные точки?\",\n \"thoroughDescription\": \"Удаляет контрольные точки ({{count}}), игнорируя хранение автоматических точек. HEAD каждого сеанса сохраняется.\",\n \"thoroughConfirm\": \"Полностью сжать\",\n \"thoroughSuccess\": \"При полном сжатии удалено контрольных точек: {{count}}\",\n \"action\": \"Очистить точки\",\n \"title\": \"Очистить точки согласно политике хранения?\",\n \"description\": \"Удаляет просроченные контрольные точки ({{count}}) согласно текущей политике хранения. HEAD каждого сеанса сохраняется.\",\n \"confirm\": \"Очистить\",\n \"success\": \"Удалено: {{count}}\"\n },\n \"reset\": {\n \"action\": \"Сбросить данные\",\n \"title\": \"Сбросить все данные?\",\n \"description\": \"Все точки будут удалены навсегда, автоматическое создание будет отключено.\",\n \"confirm\": \"Сбросить\",\n \"success\": \"Данные сброшены\"\n },\n \"restore\": {\n \"action\": \"Восстановить\",\n \"title\": \"Восстановить контрольную точку\",\n \"preview\": \"Предпросмотр\",\n \"confirm\": \"Восстановить\",\n \"conversation\": \"Диалог\",\n \"memory\": \"Память\",\n \"files\": \"Файлы рабочей области\",\n \"refreshWarning\": \"Диалог изменится. После восстановления обновите его.\",\n \"selectedCount\": \"Выбрано файлов: {{count}}\",\n \"selectAll\": \"Выбрать все изменённые файлы\",\n \"delete\": \"Удалить\",\n \"restore\": \"Восстановить\",\n \"noFileChanges\": \"Изменения файлов не требуются.\",\n \"success\": \"Точка восстановлена. Обновите диалог, чтобы загрузить состояние.\"\n }\n },\n \"workspace\": {\n \"title\": \"Файлы\",\n \"workspacePath\": \"Рабочее пространство:\",\n \"noFiles\": \"Нет файлов\",\n \"coreFiles\": \"Основные файлы\",\n \"coreFilesDesc\": \"Базовые файлы с описанием персоны, идентичности и инструкций по инструментам.\",\n \"uploadTooltip\": \"Только ZIP-файлы\",\n \"uploadTooltipWithLimit\": \"Только ZIP-файлы, максимум {{limit}} МБ\",\n \"selectFile\": \"Выберите файл для редактирования\",\n \"fileContent\": \"Содержимое файла...\",\n \"uploadSuccess\": \"Файл успешно загружен\",\n \"uploadFailed\": \"Не удалось загрузить файл\",\n \"downloadPreparing\": \"Подготовка к скачиванию рабочего пространства...\",\n \"downloadSuccess\": \"Рабочее пространство успешно скачано\",\n \"downloadFailed\": \"Не удалось скачать рабочее пространство\",\n \"zipOnly\": \"Для загрузки поддерживаются только файлы .zip\",\n \"fileSizeExceeded\": \"Размер файла превышает лимит {{limit}} МБ. Текущий файл: {{size}} МБ\",\n \"systemPromptToggleTooltip\": \"Включить/выключить загрузку этого файла в системный промпт\",\n \"memoryFileWarning\": \"MEMORY.md обычно запрашивается агентом по требованию с помощью инструментов. Загрузка его в системный промпт может привести к слишком длинному контексту.\",\n \"configUpdated\": \"Конфигурация системного промпта обновлена\",\n \"configUpdateFailed\": \"Не удалось обновить конфигурацию системного промпта\",\n \"attribution\": \"Дизайн рабочего пространства частично вдохновлён проектом OpenClaw — спасибо! 🐾\",\n \"saveSuccess\": \"Сохранено успешно\",\n \"saveFailed\": \"Не удалось сохранить\"\n },\n \"skills\": {\n \"title\": \"Навыки\",\n \"description\": \"Управляйте навыками и возможностями агента.\",\n \"qwenpawManaged\": \"Под управлением QwenPaw\",\n \"qwenpawManagedHint\": \"Доступны для изменения и передаются поддерживаемым сторонним агентам при запуске.\",\n \"providerManaged\": \"Навыки стороннего агента\",\n \"providerManagedHint\": \"Обнаружены у текущего стороннего агента и доступны только для чтения.\",\n \"providerOnly\": \"Только текущий сторонний агент\",\n \"readOnly\": \"Только чтение\",\n \"noDescription\": \"Описание отсутствует.\",\n \"providerSkillDetails\": \"Сведения о Skill стороннего агента\",\n \"providerManagedDetailHint\": \"Этот Skill управляется сторонним агентом. QwenPaw может отображать и использовать его, но не изменять.\",\n \"provider\": \"Сторонний агент\",\n \"scope\": \"Область\",\n \"statusLabel\": \"Статус\",\n \"descriptionLabel\": \"Описание\",\n \"importHub\": \"Загрузить по URL\",\n \"supportedSources\": \"Поддерживаемые источники:\",\n \"importSkills\": \"Импорт навыка\",\n \"enterSkillUrl\": \"Введите URL навыка\",\n \"supportedSkillUrlSources\": \"Поддерживаемые маркетплейсы навыков:\",\n \"urlExamples\": \"Примеры URL:\",\n \"urlValid\": \"Действительный URL от {{source}}\",\n \"invalidUrl\": \"Недействительный URL\",\n \"clickToFill\": \"Нажмите, чтобы заполнить URL\",\n \"invalidSkillUrlSource\": \"Пожалуйста, убедитесь, что URL навыка из поддерживаемого маркетплейса\",\n \"type\": \"Тип\",\n \"installedFrom\": \"Установлено из\",\n \"source\": \"Источник\",\n \"path\": \"Путь\",\n \"tags\": \"Теги\",\n \"builtin\": \"Встроенный\",\n \"custom\": \"Пользовательский\",\n \"addSkill\": \"Добавить навык\",\n \"createSkill\": \"Создать навык\",\n \"viewSkill\": \"Просмотреть навык\",\n \"editSkill\": \"Редактировать навык\",\n \"skillName\": \"Название навыка\",\n \"skillContent\": \"Содержимое навыка\",\n \"pleaseInputName\": \"Пожалуйста, введите название навыка\",\n \"pleaseInputContent\": \"Пожалуйста, введите содержимое навыка\",\n \"skillNamePlaceholder\": \"например, weather_query\",\n \"contentPlaceholder\": \"[Формат]\\n---\\nname: skill_name (обязательно, строчные буквы с подчёркиванием)\\ndescription: Краткое описание (обязательно)\\n---\\n\\nРеализация навыка (формат Markdown)\\n\\n[Пример]\\n---\\nname: weather_query\\ndescription: Запрос информации о погоде для города\\n---\\n\\n## Функции\\nЗапрос данных о погоде в реальном времени.\\n\\n## Использование\\nПользователь вводит название города, возвращается информация о погоде.\",\n \"createSuccess\": \"Навык успешно создан\",\n \"createFailed\": \"Не удалось создать навык\",\n \"deleteConfirm\": \"Вы уверены, что хотите удалить этот навык?\",\n \"deleteSuccess\": \"Навык успешно удалён\",\n \"deleteFailed\": \"Не удалось удалить навык\",\n \"batchEnable\": \"Включить\",\n \"batchDisable\": \"Отключить\",\n \"batchEnableSuccess\": \"{{count}} навыков включено\",\n \"batchEnablePartial\": \"{{enabled}} включено, {{failed}} не удалось\",\n \"batchEnableFailed\": \"Не удалось выполнить пакетное включение\",\n \"batchDisableSuccess\": \"{{count}} навыков отключено\",\n \"batchDisablePartial\": \"{{disabled}} отключено, {{failed}} не удалось\",\n \"batchDisableFailed\": \"Не удалось выполнить пакетное отключение\",\n \"batchDeleteTitle\": \"Удалить {{count}} навыков?\",\n \"batchDeleteSuccess\": \"{{count}} навыков удалено\",\n \"batchDeletePartial\": \"{{deleted}} удалено, {{failed}} не удалось\",\n \"batchDeleteFailed\": \"Не удалось выполнить пакетное удаление\",\n \"selectedCount\": \"Выбрано {{count}}\",\n \"selectAll\": \"Выбрать все\",\n \"clearSelection\": \"Очистить\",\n \"updateSuccess\": \"Навык успешно обновлён\",\n \"updateFailed\": \"Не удалось обновить навык\",\n \"frontmatterRequired\": \"Навык должен начинаться и заканчиваться ---\",\n \"frontmatterNameRequired\": \"В навыке отсутствует обязательное поле: name\",\n \"frontmatterDescriptionRequired\": \"В навыке отсутствует обязательное поле: description\",\n \"editNotSupported\": \"Не удалось отредактировать навык\",\n \"editNote\": \"Подсказка: здесь можно редактировать содержимое навыка, каналы и конфигурацию.\",\n \"create\": \"Создать\",\n \"optimizeWithAI\": \"AI Оптимизация\",\n \"stopOptimize\": \"Стоп\",\n \"optimizeSuccess\": \"Навык успешно оптимизирован\",\n \"optimizeFailed\": \"Не удалось оптимизировать навык\",\n \"noContentToOptimize\": \"Нет содержимого для оптимизации\",\n \"cancelImport\": \"Отменить импорт\",\n \"importCancelled\": \"Импорт навыка отменён\",\n \"importTimeout\": \"Время ожидания импорта навыка истекло. Повторите попытку или проверьте сеть.\",\n \"uploadSkill\": \"Загрузить навык\",\n \"uploadZip\": \"Загрузить Zip\",\n \"uploadSuccess\": \"Навык успешно загружен\",\n \"uploadFailed\": \"Не удалось загрузить навык\",\n \"uploadNoChange\": \"Новые навыки не были импортированы. Возможно, они уже существуют\",\n \"zipOnly\": \"Поддерживается загрузка только .zip файлов\",\n \"fileSizeExceeded\": \"Размер файла превышает лимит {{limit}}МБ. Текущий файл: {{size}}МБ\",\n \"channels\": \"Применимые каналы\",\n \"allChannels\": \"Все\",\n \"lastUpdated\": \"Обновлено\",\n \"searchPlaceholder\": \"Фильтр по названию\",\n \"listView\": \"Список\",\n \"gridView\": \"Сетка\",\n \"refreshHint\": \"Обновить навыки с диска\",\n \"poolSync\": \"Статус синхронизации\",\n \"uploadedToPool\": \"Загружено в пул навыков\",\n \"downloadedToWorkspace\": \"Скачано в текущее рабочее пространство\",\n \"builtinUpgradeTitle\": \"Обновить встроенный навык\",\n \"builtinUpgradeContent\": \"В пуле доступна новая версия встроенного навыка «{{name}}». Перезаписать текущую версию?\",\n \"languageSwitchTitle\": \"Переключить язык навыка\",\n \"languageSwitchContent\": \"Следующие навыки имеют другую языковую версию в пуле и будут перезаписаны:\",\n \"overwriteExistingList\": \"Следующие навыки уже существуют и будут перезаписаны:\",\n \"nameConflict\": \"Обнаружен конфликт имён. Предложенное переименование заполнено.\",\n \"uploadToPool\": \"Синхронизировать с пулом навыков\",\n \"uploadToPoolHint\": \"Загрузить выбранные навыки рабочего пространства в общий локальный пул навыков\",\n \"downloadFromPool\": \"Загрузить из пула навыков\",\n \"emptyStateBadge\": \"Новые рабочие пространства начинают пустыми\",\n \"emptyStateTitle\": \"У этого агента ещё нет навыков\",\n \"emptyStateText\": \"Это нормально. Новые рабочие пространства не содержат навыков автоматически. Загрузите навыки из локального пула или создайте новый навык здесь, чтобы оснастить этого агента.\",\n \"emptyStateDownload\": \"Загрузить из пула навыков\",\n \"emptyStateCreate\": \"Создать первый навык\",\n \"selectWorkspaceSkill\": \"Выберите навык рабочего пространства\",\n \"selectPoolItem\": \"Выберите элемент пула навыков\",\n \"renameOptional\": \"Переименование (необязательно)\",\n \"renamePlaceholder\": \"Предложенное переименование появится здесь при конфликте\",\n \"renameSingleOnly\": \"Переименование доступно только при выборе ровно одного навыка.\",\n \"downloadSkillPool\": \"Скачать пул навыков\",\n \"uploadSkillPool\": \"Загрузить пул навыков\",\n \"config\": \"Конфигурация\",\n \"configPlaceholder\": \"{\\\"KEY\\\": \\\"value\\\"}\",\n \"configInvalidJson\": \"Неверный формат JSON\",\n \"configSaved\": \"Конфигурация сохранена\",\n \"loadFailed\": \"Не удалось загрузить навыки\",\n \"refreshFailed\": \"Не удалось обновить навыки\",\n \"createdSuccessfully\": \"Создано успешно\",\n \"saveFailed\": \"Не удалось сохранить\",\n \"importFailed\": \"Не удалось импортировать\",\n \"disabledSuccessfully\": \"Отключено успешно\",\n \"enabledSuccessfully\": \"Включено успешно\",\n \"operationFailed\": \"Операция не удалась\",\n \"provideUrl\": \"Пожалуйста, укажите URL навыка хаба\",\n \"validUrl\": \"Пожалуйста, введите действительный URL, начинающийся с http:// или https://\",\n \"importedSkill\": \"Импортирован навык: {{name}}\",\n \"batchOperation\": \"Пакетная операция\",\n \"exitBatch\": \"Выйти из режима\",\n \"noSearchResults\": \"Навыки, соответствующие вашему запросу, не найдены\",\n \"filterByTag\": \"Фильтр по тегу\",\n \"noTags\": \"Нет доступных тегов\",\n \"enabledSkills\": \"Активные навыки\",\n \"disabledSkills\": \"Неактивные навыки\",\n \"active\": \"активно\"\n },\n \"skillPool\": {\n \"description\": \"Общий локальный пул навыков для встроенных навыков и загрузок из рабочих пространств. Внешние импорты (ClawHub, LobeHub) — отдельная функция.\",\n \"broadcast\": \"Трансляция\",\n \"broadcastHint\": \"Транслировать навыки пула во все или выбранные рабочие пространства\",\n \"refreshHint\": \"Обновить пул навыков с диска\",\n \"deleteExternalConfirm\": \"Файлы внешнего навыка в {{path}} будут удалены с диска.\",\n \"deleteExternalBatchWarning\": \"Выбранные внешние навыки будут безвозвратно удалены с диска.\",\n \"broadcastTitle\": \"Транслировать {{name}}\",\n \"importBuiltin\": \"Импорт встроенных\",\n \"importBuiltinHint\": \"Импортировать встроенные навыки из исходников в пул\",\n \"importBuiltinAlertHint\": \"Требуют внимания {{count}} изменений встроенных навыков\",\n \"importBuiltinSuccess\": \"Встроенные навыки импортированы: {{names}}\",\n \"importBuiltinConflicts\": \"Пропущено из-за конфликтов имён: {{names}}\",\n \"importBuiltinNoChanges\": \"Нет встроенных изменений для импорта\",\n \"importBuiltinFailed\": \"Не удалось импортировать встроенные навыки\",\n \"importBuiltinConflictTitle\": \"Обнаружены конфликты встроенных навыков\",\n \"importBuiltinConflictContent\": \"Следующие встроенные навыки отличаются от текущих копий в пуле. Подтвердите, чтобы перезаписать их версией из исходников.\",\n \"sourceVersion\": \"Версия в исходниках\",\n \"currentVersion\": \"Текущая версия\",\n \"builtinLanguage\": \"Язык\",\n \"langDefault\": \"По умолчанию\",\n \"langDefaultTooltip\": \"Сохранить текущий язык каждого навыка\",\n \"langEn\": \"Английский\",\n \"langZh\": \"Китайский\",\n \"builtinLanguageChangeTitle\": \"Сменить язык встроенного навыка?\",\n \"builtinLanguageChangeContent\": \"Перезаписать копию «{{name}}» в пуле встроенной версией {{language}}?\",\n \"builtinLanguageChangeSuccess\": \"«{{name}}» переключён на {{language}}\",\n \"builtinLanguageChangeFailed\": \"Не удалось переключить язык встроенного навыка\",\n \"importStatusMissing\": \"Отсутствует в пуле\",\n \"importStatusCurrent\": \"Уже актуален\",\n \"importStatusOutdated\": \"Устарел\",\n \"importStatusLanguageSwitchTo\": \"Перезаписать версией на {{language}}\",\n \"importStatusConflict\": \"Конфликт\",\n \"builtinNoticeSummary\": \"Обнаружено {{count}} изменений встроенных навыков\",\n \"builtinNoticeLineAdded\": \"Добавлено: {{names}}\",\n \"builtinNoticeLineMissing\": \"Отсутствует: {{names}}\",\n \"builtinNoticeLineUpdated\": \"Обновлено: {{names}}\",\n \"builtinNoticeLineRemoved\": \"Удалено: {{names}}\",\n \"zipImport\": \"ZIP-импорт\",\n \"create\": \"Создать\",\n \"edit\": \"Редактировать\",\n \"delete\": \"Удалить\",\n \"descriptionLabel\": \"Описание\",\n \"tags\": \"Теги\",\n \"tagsPlaceholder\": \"Выберите или введите теги\",\n \"filterByTag\": \"Фильтр по тегу\",\n \"version\": \"Версия\",\n \"createTitle\": \"Создать элемент пула навыков\",\n \"editTitle\": \"Редактировать {{name}}\",\n \"skillNamePlaceholder\": \"Название навыка\",\n \"contentPlaceholder\": \"Содержимое SKILL.md\",\n \"nameConflict\": \"Обнаружен конфликт имён. Применено предложенное переименование.\",\n \"deletedFromPool\": \"Удалено из пула навыков\",\n \"deleteBuiltinConfirm\": \"Это удалит встроенный навык из пула. Позже его можно снова импортировать из исходников.\",\n \"deleteConfirm\": \"Это удалит навык из общего локального пула навыков.\",\n \"deleteTitle\": \"Удалить {{name}}?\",\n \"batchDeleteTitle\": \"Удалить {{count}} навыков из пула?\",\n \"batchDeleteSuccess\": \"{{count}} навыков из пула удалено\",\n \"batchDeletePartial\": \"{{deleted}} удалено, {{failed}} не удалось\",\n \"batchDeleteFailed\": \"Не удалось выполнить пакетное удаление\",\n \"imported\": \"Импортировано: {{names}}\",\n \"noNewImports\": \"Новые навыки не импортированы\",\n \"allBuiltin\": \"Все встроенные\",\n \"selectWorkspaces\": \"Транслировать в рабочее пространство\",\n \"allWorkspaces\": \"Все рабочие пространства\",\n \"optionalRename\": \"Необязательное переименование\",\n \"back\": \"Назад\",\n \"skillCount\": \"{{count}} навыков\",\n \"skillName\": \"Название навыка\",\n \"status\": \"Статус\",\n \"installedFrom\": \"Установлено из\",\n \"updateBuiltin\": \"Обновить\",\n \"updateConfirmTitle\": \"Обновить {{name}}\",\n \"updateConfirmContent\": \"Обновить до версии {{version}}? Текущая копия в пуле будет заменена последней встроенной версией.\",\n \"updateSuccess\": \"Обновлено успешно\",\n \"updateFailed\": \"Не удалось обновить\",\n \"broadcastSuccess\": \"Трансляция завершена\",\n \"broadcastFailed\": \"Не удалось выполнить трансляцию\",\n \"overwriteConfirm\": \"Перезаписать существующие навыки?\",\n \"overwriteTargetsContent\": \"Следующие существующие цели будут перезаписаны:\",\n \"builtinOverwriteTargetsContent\": \"Следующие встроенные цели имеют другую версию и будут перезаписаны:\",\n \"renameAndRetry\": \"Навык с таким именем уже существует. Использовать предложенное имя?\",\n \"multiConflictTitle\": \"Обнаружено несколько конфликтов\",\n \"multiConflictDesc\": \"Следующие навыки уже существуют. Устраните конфликты по отдельности и повторите попытку:\",\n \"singleConflictRename\": \"\\\"{{name}}\\\" уже существует. Переименовать в \\\"{{suggested}}\\\" для продолжения?\",\n \"statusUpToDate\": \"Актуально\",\n \"statusSynced\": \"Синхронизировано\",\n \"statusOutdated\": \"Устарело\",\n \"statusNotSynced\": \"Не синхронизировано\",\n \"statusConflict\": \"Конфликт\",\n \"renameEntry\": \"Новое имя для \\\"{{name}}\\\":\",\n \"builtin\": \"встроенный\",\n \"custom\": \"пользовательский\"\n },\n \"mcp\": {\n \"title\": \"MCP-клиенты\",\n \"qwenpawManaged\": \"Под управлением QwenPaw\",\n \"qwenpawManagedHint\": \"Общая конфигурация панели управления внедряется в поддерживаемые сторонние агенты во время выполнения.\",\n \"providerManaged\": \"Локальные MCP {{provider}}\",\n \"providerManagedHint\": \"Обнаружены в конфигурации стороннего агента. Доступны только для чтения и только этому агенту.\",\n \"providerOnly\": \"Только сторонний агент\",\n \"readOnly\": \"Только чтение\",\n \"create\": \"Создать клиент\",\n \"formatSupport\": \"Поддерживаемые форматы\",\n \"standardFormat\": \"Стандартный (обёртка mcpServers)\",\n \"directFormat\": \"Прямой (ключ → объект конфигурации)\",\n \"singleFormat\": \"Один клиент (плоские поля)\",\n \"emptyState\": \"MCP-клиенты пока не настроены\",\n \"loadError\": \"Не удалось загрузить MCP-клиентов\",\n \"createSuccess\": \"MCP-клиент успешно создан\",\n \"createError\": \"Не удалось создать MCP-клиент\",\n \"updateSuccess\": \"MCP-клиент успешно обновлён\",\n \"updateError\": \"Не удалось обновить MCP-клиент\",\n \"enableSuccess\": \"MCP-клиент успешно включён\",\n \"disableSuccess\": \"MCP-клиент успешно отключён\",\n \"toggleError\": \"Не удалось переключить статус MCP-клиента\",\n \"deleteConfirm\": \"Вы уверены, что хотите удалить этот MCP-клиент?\",\n \"deleteSuccess\": \"MCP-клиент успешно удалён\",\n \"deleteError\": \"Не удалось удалить MCP-клиент\",\n \"maskedFieldHint\": \"Чувствительные поля скрыты ***. Редактирование скрытых значений не поддерживается — замените полным новым значением.\",\n \"tools\": \"Инструменты и разрешения\",\n \"noTools\": \"На этом MCP сервере нет доступных инструментов\",\n \"toolsLoadError\": \"Не удалось загрузить инструменты с MCP сервера\",\n \"toolsConnecting\": \"MCP сервер подключается, попробуйте через некоторое время\",\n \"toolSchema\": \"Схема ввода\",\n \"access\": {\n \"title\": \"Доступ\",\n \"button\": \"Доступ\",\n \"summary\": \"Политика доступа MCP\",\n \"clientSection\": \"Общий доступ\",\n \"toolSection\": \"Доступ к инструменту\",\n \"default\": \"По умолчанию\",\n \"sourceType\": \"Источник\",\n \"sourceValue\": \"Значение источника\",\n \"sourceValueAllChannels\": \"Все каналы\",\n \"subjectType\": \"Объект\",\n \"subjectValue\": \"Значение объекта\",\n \"effectLabel\": \"Политика\",\n \"source\": {\n \"channel\": \"Канал\"\n },\n \"subjectTypeOption\": {\n \"all\": \"Все\",\n \"user\": \"Пользователь\"\n },\n \"sourceValuePlaceholder\": {\n \"channel\": \"console / dingtalk\"\n },\n \"subjectValuePlaceholder\": \"например, alice\",\n \"subjectValuePlaceholderSelectedSource\": \"ID пользователя в выбранном источнике\",\n \"subjectValuePlaceholderScoped\": \"ID пользователя в {{source}}\",\n \"subjectValueAll\": \"Все\",\n \"recentUserPlaceholder\": \"ID пользователя относится к выбранному источнику; введите или выберите заново\",\n \"noRecentUsers\": \"Для этого источника нет недавних пользователей; введите ID вручную\",\n \"ambiguousUserSourceWarning\": \"ID пользователя уникален только внутри источника. Выберите конкретный канал, чтобы избежать совпадений между каналами.\",\n \"unknownUserValueWarning\": \"Этого ID нет среди недавних активных пользователей. Сохранение разрешено.\",\n \"validation\": {\n \"missingUserValue\": \"Выберите конкретный источник и введите корректный ID пользователя.\",\n \"ambiguousUserSource\": \"Правила для пользователя должны указывать конкретный канал, а не все каналы.\",\n \"unknownUserValue\": \"Этого ID нет среди недавних активных пользователей. Сохранение разрешено.\"\n },\n \"addRule\": \"Добавить правило\",\n \"deleteRule\": \"Удалить правило\",\n \"noClientRules\": \"Нет правил\",\n \"noRules\": \"Нет правил, используется политика по умолчанию\",\n \"inherit\": \"Наследовать ({{effect}})\",\n \"stale\": \"Не найдено\",\n \"disabledTools\": \"Этот MCP клиент отключен; сохранённые правила доступа всё ещё можно редактировать.\",\n \"discardTitle\": \"Отменить несохранённые изменения?\",\n \"discardContent\": \"Несохранённые изменения политики доступа будут потеряны.\",\n \"loadError\": \"Не удалось загрузить политику доступа MCP\",\n \"saveSuccess\": \"Политика доступа MCP сохранена\",\n \"saveError\": \"Не удалось сохранить политику доступа MCP\",\n \"unmanagedRules\": \"{{count}} расширенных правил\",\n \"effect\": {\n \"allow\": \"Разрешить\",\n \"ask\": \"Спрашивать\",\n \"deny\": \"Запретить\"\n }\n },\n \"tab\": {\n \"json\": \"Импорт JSON\",\n \"form\": \"Режим формы\"\n },\n \"form\": {\n \"key\": \"Ключ клиента\",\n \"keyPlaceholder\": \"my-mcp-server\",\n \"keyRequired\": \"Ключ клиента обязателен\",\n \"name\": \"Отображаемое имя\",\n \"namePlaceholder\": \"My MCP Server\",\n \"nameRequired\": \"Отображаемое имя обязательно\",\n \"transport\": \"Транспорт\",\n \"url\": \"URL сервера\",\n \"urlRequired\": \"URL обязателен для удалённых MCP серверов\",\n \"command\": \"Команда\",\n \"commandRequired\": \"Команда обязательна для транспорта stdio\",\n \"args\": \"Аргументы (через пробел или с новой строки)\",\n \"description\": \"Описание\",\n \"descriptionPlaceholder\": \"Необязательное описание\",\n \"env\": \"Переменные окружения\",\n \"envPlaceholder\": \"KEY=VALUE (по одной на строку)\"\n },\n \"oauth\": {\n \"enableOAuth\": \"Использовать OAuth авторизацию\",\n \"manage\": \"OAuth\",\n \"authorized\": \"Авторизован\",\n \"notAuthorized\": \"Не авторизован\",\n \"expired\": \"Токен истёк\",\n \"failed\": \"Ошибка авторизации\",\n \"waiting\": \"Ожидание авторизации...\",\n \"authorize\": \"Авторизовать\",\n \"reauthorize\": \"Переавторизовать\",\n \"revoke\": \"Отозвать\",\n \"startFailed\": \"Не удалось запустить OAuth\",\n \"authFailed\": \"OAuth авторизация не удалась\",\n \"popupBlocked\": \"Всплывающее окно заблокировано браузером. Разрешите всплывающие окна и повторите попытку.\",\n \"windowClosed\": \"Окно авторизации закрыто. Повторите попытку.\",\n \"noUrl\": \"Сначала введите URL сервера\",\n \"noClientKey\": \"Сначала создайте клиент\",\n \"showAdvanced\": \"Дополнительные параметры\",\n \"hideAdvanced\": \"Скрыть параметры\",\n \"clientId\": \"Client ID (необязательно)\",\n \"clientIdPlaceholder\": \"Оставьте пустым для динамической регистрации\",\n \"scope\": \"Scope (необязательно)\",\n \"scopePlaceholder\": \"openid profile email\",\n \"authEndpoint\": \"Authorization Endpoint (переопределить)\",\n \"tokenEndpoint\": \"Token Endpoint (переопределить)\",\n \"endpointHint\": \"Оставьте пустым для автообнаружения. Заполните для ручного указания.\"\n }\n },\n \"heartbeat\": {\n \"title\": \"Пульс\",\n \"description\": \"Запускайте HEARTBEAT.md с фиксированным интервалом для самопроверок. По умолчанию выполняется тихо и не влияет на текущие диалоги, либо может отправлять ответы в последний канал чата.\",\n \"enabled\": \"Включить пульс\",\n \"every\": \"Интервал\",\n \"everyRequired\": \"Обязательно\",\n \"everyMin\": \"Должно быть не меньше 1\",\n \"timeoutSeconds\": \"Таймаут выполнения (секунды)\",\n \"timeoutRequired\": \"Обязательно\",\n \"timeoutMin\": \"Должно быть не меньше 1\",\n \"timeoutMax\": \"Должно быть не больше 3600\",\n \"unitMinutes\": \"Минуты\",\n \"unitHours\": \"Часы\",\n \"target\": \"Цель ответа\",\n \"targetMain\": \"Тихий режим (по умолчанию, без вывода в канал)\",\n \"targetLast\": \"Отправлять в последний канал чата\",\n \"activeHours\": \"Активные часы (необязательно)\",\n \"activeStart\": \"Время начала\",\n \"activeEnd\": \"Время окончания\",\n \"loadFailed\": \"Не удалось загрузить конфигурацию пульса\",\n \"saveSuccess\": \"Успешно сохранено; пульс перезагружен на лету\",\n \"saveFailed\": \"Не удалось сохранить конфигурацию пульса\",\n \"targetInbox\": \"Отправлять во входящие\",\n \"runNow\": \"Запустить сейчас\",\n \"runNowSuccess\": \"Пульс запущен. Через несколько секунд проверьте входящие или целевой канал.\",\n \"runNowFailed\": \"Не удалось запустить выполнение пульса\"\n },\n \"cronJobs\": {\n \"title\": \"Cron-задачи\",\n \"description\": \"Создавайте и управляйте задачами по расписанию, которые автоматически выполняются в указанное время.\",\n \"createJob\": \"Создать задачу\",\n \"editJob\": \"Редактировать задачу\",\n \"confirmDelete\": \"Подтвердить удаление\",\n \"deleteConfirm\": \"Вы уверены, что хотите удалить эту Cron Job?\",\n \"okText\": \"OK\",\n \"cancelText\": \"Отмена\",\n \"deleteText\": \"Удалить\",\n \"id\": \"ID задачи\",\n \"name\": \"Название задачи\",\n \"enabled\": \"Статус\",\n \"scheduleType\": \"Тип расписания\",\n \"scheduleCron\": \"Расписание (Cron)\",\n \"scheduleCronLabel\": \"Расписание (Cron)\",\n \"scheduleTimezone\": \"Часовой пояс\",\n \"cronType\": \"Частота\",\n \"cronTypeHourly\": \"Каждый час\",\n \"cronTypeDaily\": \"Ежедневно\",\n \"cronTypeWeekly\": \"Еженедельно\",\n \"cronTypeCustom\": \"Пользовательский\",\n \"cronTime\": \"Время\",\n \"cronDaysOfWeek\": \"Дни недели\",\n \"cronDayMon\": \"Понедельник\",\n \"cronDayTue\": \"Вторник\",\n \"cronDayWed\": \"Среда\",\n \"cronDayThu\": \"Четверг\",\n \"cronDayFri\": \"Пятница\",\n \"cronDaySat\": \"Суббота\",\n \"cronDaySun\": \"Воскресенье\",\n \"cronCustomExpression\": \"Cron-выражение\",\n \"taskText\": \"Текст сообщения\",\n \"action\": \"Действие\",\n \"disable\": \"Отключить\",\n \"executeNow\": \"Выполнить сейчас\",\n \"edit\": \"Редактировать\",\n \"delete\": \"Удалить\",\n \"totalItems\": \"Всего {{count}} элементов\",\n \"perPage\": \"/ стр.\",\n \"pleaseInputId\": \"Пожалуйста, введите ID задачи\",\n \"pleaseInputName\": \"Пожалуйста, введите название задачи\",\n \"pleaseInputCron\": \"Пожалуйста, введите cron-выражение\",\n \"pleaseSelectTaskType\": \"Пожалуйста, выберите тип задачи\",\n \"pleaseInputRequest\": \"Пожалуйста, введите содержимое запроса\",\n \"pleaseInputMessageContent\": \"Пожалуйста, введите текст сообщения\",\n \"pleaseInputChannel\": \"Пожалуйста, введите целевой канал\",\n \"pleaseInputUserId\": \"Пожалуйста, введите ID целевого пользователя\",\n \"pleaseInputSessionId\": \"Пожалуйста, введите ID целевой сессии\",\n \"jobIdPlaceholder\": \"например, daily-report-job\",\n \"jobNamePlaceholder\": \"например, Ежедневный утренний отчёт\",\n \"selectTimezone\": \"Выберите часовой пояс\",\n \"taskDescriptionPlaceholder\": \"Для текстовых задач введите текст сообщения...\",\n \"invalidJsonFormat\": \"Неверный формат JSON\",\n \"jsonFormatRequired\": \"Требуется формат JSON\",\n \"taskType\": \"Тип задачи\",\n \"text\": \"Текст сообщения\",\n \"requestInput\": \"Содержимое запроса\",\n \"requestSessionId\": \"ID сессии запроса\",\n \"requestUserId\": \"ID пользователя запроса\",\n \"dispatchChannel\": \"Целевой канал\",\n \"dispatchTargetUserId\": \"ID целевого пользователя\",\n \"dispatchTargetSessionId\": \"ID целевой сессии\",\n \"dispatchMode\": \"Режим доставки\",\n \"silentDelivery\": \"Тихая доставка\",\n \"runtimeShareSession\": \"Общая сессия\",\n \"runtimeMaxConcurrency\": \"Макс. параллельность\",\n \"runtimeTimeoutSeconds\": \"Таймаут (секунды)\",\n \"runtimeMisfireGraceSeconds\": \"Льготный период пропуска (секунды)\",\n \"runtimeToolSafety\": \"Проверка безопасности инструментов\",\n \"idTooltip\": \"Генерируемый системой, нередактируемый уникальный UUID, присваиваемый при создании задачи.\",\n \"nameTooltip\": \"Понятное имя, чтобы вам было проще найти эту задачу.\",\n \"cronTooltip\": \"Определите, когда задача должна запускаться\",\n \"cronExample\": \"Популярные примеры: '0 9 * * *' = ежедневно в 09:00 | '*/30 * * * *' = каждые 30 мин | '0 */2 * * *' = каждые 2 часа | '0 0 * * 0' = воскресенье, полночь\",\n \"cronHelper\": \"Впервые работаете с cron-выражениями?\",\n \"cronHelperLink\": \"Использовать онлайн-генератор\",\n \"timezoneTooltip\": \"Часовой пояс для cron-расписания. По умолчанию: UTC\",\n \"taskTypeTooltip\": \"Выберите 'text' для простых текстовых задач или 'agent' для сложных сценариев с агентом.\",\n \"textTooltip\": \"Простая текстовая задача: здесь указывается фактический текст сообщения. Обязательно при типе задачи «text».\",\n \"requestInputTooltip\": \"Содержимое сообщения в формате JSON. Именно это агент получит и обработает. Обязательно при типе задачи «agent».\",\n \"requestInputExample\": \"Формат: [{\\\"role\\\":\\\"user\\\",\\\"content\\\":[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Ваше сообщение\\\"}]}]\",\n \"requestSessionIdTooltip\": \"ID сессии для контекста запроса. Если не уверены, используйте 'default'.\",\n \"requestUserIdTooltip\": \"ID пользователя, который инициирует запрос. Для автоматических задач используйте 'system'.\",\n \"dispatchChannelTooltip\": \"Целевой канал, куда будет отправлен ответ (например, 'console', 'discord', 'imessage').\",\n \"dispatchTargetUserIdTooltip\": \"ID пользователя, который получит ответ в целевом канале.\",\n \"dispatchTargetSessionIdTooltip\": \"ID сессии, куда будет доставлен ответ в целевом канале.\",\n \"dispatchModeTooltip\": \"Выберите 'stream' для ответов в реальном времени или 'final' только для итоговых ответов.\",\n \"silentDeliveryTooltip\": \"Выполнить задачу агента с сохранением сессии и трассировки, но не отправлять результат в канал.\",\n \"shareSessionTooltip\": \"При включении используется общая сессия с целевым пользователем. При отключении каждый запуск создает изолированный контекст с уникальным ID. Подходит для независимых задач без памяти. По умолчанию: включено\",\n \"maxConcurrencyTooltip\": \"Максимальное количество одновременно выполняемых экземпляров этой задачи. По умолчанию: 1\",\n \"timeoutSecondsTooltip\": \"Максимальное время выполнения в секундах. При превышении задача будет остановлена.\",\n \"misfireGraceSecondsTooltip\": \"Льготный период для пропущенных запусков. Если задача пропустила время запуска больше чем на это значение, она не будет выполнена.\",\n \"toolSafetyTooltip\": \"При включении рискованные вызовы инструментов требуют одобрения пользователя (может блокировать автоматические задачи). При выключении все инструменты выполняются без одобрения. По умолчанию: выключено\",\n \"executeNowTitle\": \"Выполнить задачу сейчас\",\n \"executeNowContent\": \"Вы уверены, что хотите выполнить «{{name}}» прямо сейчас?\",\n \"executeNowConfirm\": \"Выполнить сейчас\",\n \"createFromTemplate\": \"Создать из шаблона\",\n \"templateModalTitle\": \"Выберите шаблон задачи\",\n \"templateModalDescription\": \"Выберите встроенный шаблон и при необходимости измените ключевые параметры на панели перед сохранением.\",\n \"useTemplate\": \"Использовать шаблон\",\n \"templateTagPersonal\": \"Личное\",\n \"templateTagTeam\": \"Команда\",\n \"templateTagReminder\": \"Напоминание\",\n \"templateTagCalendar\": \"Календарь\",\n \"templateTagCalendarRecommended\": \"Рекомендуется для календаря\",\n \"saveResultToInbox\": \"Сохранять результат во входящих\",\n \"saveResultToInboxTooltip\": \"Если включено, успешные выполнения с успешной доставкой сохраняются во входящих. Ошибки доставки всегда записываются во входящие как резерв.\",\n \"scheduleFilterAll\": \"Все типы расписания\",\n \"scheduleTypeRecurring\": \"Повторяющаяся\",\n \"scheduleTypeOnce\": \"Запланированная\",\n \"onceRunAt\": \"Запуск в\",\n \"repeatEnabled\": \"Повтор\",\n \"repeatEnabledTooltip\": \"Включить повторяющееся расписание от этого времени начала\",\n \"repeatFrequency\": \"Частота повтора\",\n \"repeatEveryDays\": \"Повторять каждые (дни)\",\n \"repeatEveryPrefix\": \"Каждые\",\n \"repeatEverySuffix\": \"дн.\",\n \"repeatEndType\": \"Окончание повтора\",\n \"repeatEndNever\": \"Никогда\",\n \"repeatEndUntil\": \"До даты\",\n \"repeatEndCount\": \"Ограниченное число запусков\",\n \"repeatUntil\": \"Дата окончания\",\n \"repeatCount\": \"Число запусков\",\n \"pleaseSelectScheduleType\": \"Выберите тип расписания\",\n \"pleaseInputRunAt\": \"Выберите время запуска\",\n \"pleaseInputRepeatEveryDays\": \"Введите интервал повтора (дни)\",\n \"pleaseSelectRepeatEndType\": \"Выберите условие окончания\",\n \"pleaseInputRepeatUntil\": \"Выберите дату окончания\",\n \"pleaseInputRepeatCount\": \"Введите число запусков\",\n \"validation\": {\n \"cronRequired\": \"Для повторяющегося расписания требуется cron-выражение.\",\n \"runAtRequired\": \"Для одноразового расписания требуется время выполнения.\",\n \"repeatUntilRequired\": \"При выборе «До даты» необходимо указать дату окончания.\",\n \"repeatCountRequired\": \"При выборе «Ограниченное число запусков» необходимо указать количество запусков.\",\n \"repeatUntilAfterRunAt\": \"Дата окончания должна быть позже времени выполнения. Выберите дату окончания после времени выполнения.\",\n \"textRequired\": \"При типе задачи text содержимое сообщения обязательно.\",\n \"requestRequired\": \"При типе задачи agent содержимое запроса обязательно.\",\n \"invalidCronExpression\": \"Некорректное cron-выражение. Используйте формат из 5 полей.\"\n },\n \"listView\": \"Список\",\n \"calendarView\": \"Календарь\",\n \"calendarEmptyHint\": \"Запланированных задач пока нет. Создайте задачи по расписанию, чтобы увидеть их в календаре.\",\n \"calendarMoreItems\": \"+ещё {{count}}\",\n \"calendarDayListTitle\": \"Запланированные задачи на {{date}}\",\n \"executionHistory\": \"История\",\n \"historyTitle\": \"История — {{name}}\",\n \"historyEmpty\": \"Записей о выполнении пока нет.\",\n \"historyStatusSuccess\": \"Успех\",\n \"historyStatusFailed\": \"Ошибка\",\n \"historyStatusRunning\": \"Выполняется\",\n \"historyStatusCancelled\": \"Отменено\",\n \"historyTriggerScheduled\": \"Запуск по расписанию\",\n \"historyTriggerManual\": \"Ручной запуск\",\n \"historyExpand\": \"Развернуть\",\n \"historyCollapse\": \"Свернуть\",\n \"templates\": {\n \"dailyTechNewsBrief\": {\n \"title\": \"Дайджест технических новостей\",\n \"description\": \"В 09:30 в будни собирать актуальные технические заголовки в краткий обзор.\",\n \"frequency\": \"Будни 09:30 (30 9 * * 1-5)\"\n },\n \"dailyEveningReflection\": {\n \"title\": \"Вечерняя рефлексия\",\n \"description\": \"В 20:00 в будни — короткая подсказка подвести итоги рабочего дня.\",\n \"frequency\": \"Будни 20:00 (0 20 * * 1-5)\"\n },\n \"weekendRelaxationReminder\": {\n \"title\": \"Напоминание отдохнуть на выходных\",\n \"description\": \"По утрам в выходные — подборка популярных фильмов для лёгкого отдыха.\",\n \"frequency\": \"Выходные 10:00 (0 10 * * 6,0)\"\n },\n \"pomodoroBreakReminder\": {\n \"title\": \"Напоминание о перерыве (помодоро)\",\n \"description\": \"В рабочие часы каждые 25 минут напоминать попить воды или дать глазам отдохнуть.\",\n \"frequency\": \"Будни 09:00–17:59 каждые 25 мин (*/25 9-17 * * 1-5)\"\n },\n \"petCareReminder\": {\n \"title\": \"Питомец: глистогонное / прививка\",\n \"description\": \"15-го числа каждого месяца вечером — напоминание о дегельминтизации или следующей прививке.\",\n \"frequency\": \"Ежемесячно 15-го в 20:00 (0 20 15 * *)\"\n },\n \"onceTextBirthdayReminder\": {\n \"title\": \"Напоминание о дне рождения\",\n \"description\": \"Напомнить, что у xx день рождения 1 января.\",\n \"frequency\": \"Выполнить: 2026-01-01 09:00\"\n },\n \"onceAgentBusinessTripPrep\": {\n \"title\": \"Подготовка к командировке\",\n \"description\": \"Проверить погоду в пункте назначения и предложить рекомендации по подготовке к поездке.\",\n \"frequency\": \"Выполнить: 2026-01-01 20:00\"\n },\n \"repeatCountTextMedicineReminder\": {\n \"title\": \"Напоминание о лекарствах (14 раз)\",\n \"description\": \"Напоминать вовремя принимать лекарства в рамках курса из 14 запусков.\",\n \"frequency\": \"Первый запуск: 2026-01-01 09:00 | Каждые 1 день | 14 запусков\"\n },\n \"repeatCountAgentDietPlan\": {\n \"title\": \"План питания на 14 дней\",\n \"description\": \"Генерировать и отправлять ежедневные рекомендации по питанию в течение 14 дней.\",\n \"frequency\": \"Первый запуск: 2026-01-01 08:00 | Каждые 1 день | 14 запусков\"\n },\n \"repeatUntilTextWeeklyMeeting\": {\n \"title\": \"Напоминание о еженедельной встрече (2 месяца)\",\n \"description\": \"Еженедельно напоминать о встрече до установленной даты окончания.\",\n \"frequency\": \"Первый запуск: 2026-01-02 08:45 | Каждые 7 дней | До 2026-03-01\"\n },\n \"repeatUntilAgentWeeklySummary\": {\n \"title\": \"Недельный итог перед встречей\",\n \"description\": \"Перед каждой недельной встречей формировать рабочий итог на основе memory за последнюю неделю.\",\n \"frequency\": \"Первый запуск: 2026-01-02 08:30 | Каждые 7 дней | До 2026-03-01\"\n }\n }\n },\n \"channels\": {\n \"title\": \"Каналы\",\n \"description\": \"Управление и настройка каналов сообщений\",\n \"loading\": \"Загрузка каналов...\",\n \"configSaved\": \"Конфигурация успешно сохранена\",\n \"configFailed\": \"Не удалось сохранить конфигурацию\",\n \"channelType\": \"Тип канала\",\n \"status\": \"Статус\",\n \"totalItems\": \"Всего {{count}} элементов\",\n \"botPrefix\": \"Префикс бота\",\n \"showToolCalls\": \"Показывать сведения о вызове инструмента\",\n \"showToolCallsTooltip\": \"Показывать пользователям сведения о вызове инструмента (выключите, чтобы скрыть)\",\n \"showToolResults\": \"Показывать сведения о результате инструмента\",\n \"showToolResultsTooltip\": \"Показывать пользователям сведения о результате инструмента (выключите, чтобы скрыть)\",\n \"toolCallMaxLength\": \"Максимальная длина отображения вызова инструмента\",\n \"toolResultMaxLength\": \"Максимальная длина отображения результата инструмента\",\n \"toolMaxLengthTooltip\": \"Установите 0, чтобы не ограничивать длину\",\n \"onebotMediaBase64\": \"Отправлять локальные медиа как Base64\",\n \"onebotMediaBase64Tooltip\": \"Выберите способ отправки медиафайлов. Если параметр выключен, отправляется путь к файлу; если включён — файл кодируется в Base64. Удалённые URL остаются без изменений.\",\n \"onebotMediaBase64MaxMb\": \"Ограничение размера файла Base64\",\n \"onebotMediaBase64MaxMbTooltip\": \"Медиафайлы больше этого ограничения отправляются с использованием исходного пути к файлу.\",\n \"onebotMediaBase64MaxMbRequired\": \"Введите ограничение размера файла Base64\",\n \"onebotMediaBase64MaxMbMin\": \"Ограничение размера файла Base64 должно быть не менее 1 MB\",\n \"showThinking\": \"Показывать рассуждения\",\n \"showThinkingTooltip\": \"Показывать пользователям рассуждения модели (выключите, чтобы скрыть)\",\n \"noTextDebounce\": \"Дебаунс без текста\",\n \"noTextDebounceTooltip\": \"Если включено, сообщения только с медиа (изображения/видео/файлы) буферизуются до прихода текстового сообщения, затем объединяются. Отключите для немедленной обработки всех сообщений. Голосовые сообщения всегда обрабатываются немедленно.\",\n \"streamingEnabled\": \"Потоковый вывод\",\n \"streamingEnabledDingtalkHint\": \"Действует только при типе сообщения Card\",\n \"streamingEnabledFeishuHint\": \"Необходимо включить разрешение cardkit:card:write на странице управления разрешениями открытой платформы Feishu\",\n \"ackMessage\": \"Сообщение подтверждения\",\n \"ackMessageTooltip\": \"Отправить мгновенный ответ при получении сообщения до начала обработки агентом. Оставьте пустым для отключения.\",\n \"ackMessagePlaceholder\": \"⏳ Обработка...\",\n \"notSet\": \"Не задано\",\n \"clickCardToEdit\": \"Нажмите на карточку для редактирования\",\n \"settings\": \"Настройки\",\n \"channelSettings\": \"Настройки канала\",\n \"pleaseInputDbPath\": \"Пожалуйста, введите путь к БД\",\n \"pleaseInputPollInterval\": \"Пожалуйста, введите интервал опроса\",\n \"discordBotToken\": \"Токен Discord-бота\",\n \"acceptBotMessages\": \"Принимать сообщения от ботов\",\n \"acceptBotMessagesTooltip\": \"При включении будут приниматься сообщения от других ботов\",\n \"httpProxyPlaceholder\": \"http://127.0.0.1:18118\",\n \"httpProxyAuthPlaceholder\": \"user:password\",\n \"dbPathPlaceholder\": \"~/Library/Messages/chat.db\",\n \"botPrefixPlaceholder\": \"@bot\",\n \"dmPolicy\": \"Политика личных сообщений\",\n \"dmPolicyTooltip\": \"Кто может взаимодействовать с ботом в личных сообщениях. Open: все; Allowlist: только авторизованные пользователи\",\n \"groupPolicy\": \"Политика групп\",\n \"groupPolicyTooltip\": \"Кто может взаимодействовать с ботом в групповых чатах. Open: все; Allowlist: только авторизованные пользователи\",\n \"requireMention\": \"Требовать @упоминание\",\n \"requireMentionTooltip\": \"Бот отвечает в групповых чатах только при явном @упоминании\",\n \"policyOpen\": \"Open\",\n \"policyAllowlist\": \"Allowlist\",\n \"allowFrom\": \"Пользователи allowlist\",\n \"allowFromTooltip\": \"Список ID пользователей, которым разрешено взаимодействовать с ботом. Включите белый список, затем напишите боту в ЛС — ваш ID будет в сообщении об отказе\",\n \"allowFromPlaceholder\": \"Введите ID пользователя и нажмите Enter, чтобы добавить\",\n \"denyMessage\": \"Сообщение об отказе\",\n \"denyMessageTooltip\": \"Пользовательское сообщение для пользователей, которым отказан доступ\",\n \"twilioAccountSid\": \"Twilio Account SID\",\n \"twilioAuthToken\": \"Twilio Auth Token\",\n \"phoneNumber\": \"Номер телефона\",\n \"phoneNumberSid\": \"Phone Number SID\",\n \"phoneNumberSidHelp\": \"Можно найти в разделе Phone Numbers → Active Numbers в Twilio Console.\",\n \"ttsProvider\": \"TTS-провайдер\",\n \"ttsVoice\": \"TTS-голос\",\n \"sttProvider\": \"STT-провайдер\",\n \"language\": \"Язык\",\n \"welcomeGreeting\": \"Приветственное сообщение\",\n \"voiceSetupGuide\": \"Создайте аккаунт Twilio, купите номер телефона и затем введите данные ниже. Account SID и Auth Token находятся на панели Twilio Console. Phone Number SID указан в разделе Phone Numbers → Active Numbers.\",\n \"voiceSetupLink\": \"Открыть Twilio Console\",\n \"sipSetupGuide\": \"Настройте SIP-регистратор (например, Asterisk, FreeSWITCH) и введите учётные данные SIP ниже. В режиме Dev pyVoIP обрабатывает SIP/RTP локально. В режиме Production используется LiveKit SIP Server.\",\n \"sipMode\": \"Режим SIP\",\n \"sipModeTooltip\": \"Dev: Python pyVoIP для локального тестирования. Production: LiveKit SIP Server для высокой нагрузки.\",\n \"sipServer\": \"SIP-сервер\",\n \"sipServerPlaceholder\": \"Оставьте пустым для встроенного регистратора\",\n \"sipServerPlaceholderLivekit\": \"Не требуется для режима LiveKit\",\n \"sipUsername\": \"Имя пользователя SIP\",\n \"sipPassword\": \"Пароль SIP\",\n \"sipPort\": \"Порт SIP\",\n \"sipTransport\": \"Транспорт\",\n \"sipDashscopeApiKey\": \"DashScope API Key\",\n \"sipDashscopeApiKeyTooltip\": \"API-ключ для Aliyun DashScope STT/TTS. Если пусто, используется переменная окружения DASHSCOPE_API_KEY.\",\n \"livekitUrl\": \"LiveKit URL\",\n \"livekitApiKey\": \"LiveKit API Key\",\n \"livekitApiSecret\": \"LiveKit API Secret\",\n \"livekitSipTrunkId\": \"LiveKit SIP Trunk ID\",\n \"livekitRoomName\": \"Имя комнаты LiveKit\",\n \"livekitRoomNameTooltip\": \"Имя комнаты LiveKit для ожидания SIP-вызовов. Должно совпадать с именем комнаты в правиле SIP Dispatch Rule.\",\n \"loginWeCom\": \"Получить QR-код WeCom\",\n \"wecomSetupGuide\": \"При создании бота через QR-код, на этапе авторизации возможностей бота выберите «Пропустить авторизацию». Если выбрать «Подтвердить авторизацию», другие пользователи, кроме создателя бота, могут не иметь возможности взаимодействовать с ботом.\",\n \"wecomScanAuth\": \"Сканировать для авторизации\",\n \"wecomAuthHint\": \"Отсканируйте QR-код выше с помощью WeCom для авторизации. Bot ID и Secret будут заполнены автоматически.\",\n \"wecomAuthSuccess\": \"Бот WeCom авторизован. Bot ID и Secret заполнены автоматически.\",\n \"wecomQrcodeFailed\": \"Не удалось получить QR-код WeCom. Попробуйте ещё раз.\",\n \"slackBotToken\": \"Bot Token\",\n \"slackBotTokenTooltip\": \"Slack Bot User OAuth Token, начинается с xoxb-\",\n \"slackAppToken\": \"App Token\",\n \"slackAppTokenTooltip\": \"Slack App-Level Token для Socket Mode, начинается с xapp-\",\n \"slackProxyTooltip\": \"URL HTTP-прокси для подключения к Slack API\",\n \"dingtalkSetupGuide\": \"Отсканируйте QR-код в DingTalk для мгновенного создания бота. Приложение DingTalk будет создано автоматически, а Client ID и Client Secret будут заполнены.\",\n \"dingtalkScanAuth\": \"Сканировать для создания бота\",\n \"dingtalkGetQrcode\": \"Получить QR-код DingTalk\",\n \"dingtalkScanHint\": \"Отсканируйте QR-код выше с помощью DingTalk. Client ID и Client Secret будут заполнены автоматически после авторизации.\",\n \"dingtalkAuthSuccess\": \"Бот DingTalk создан. Client ID и Client Secret заполнены автоматически.\",\n \"dingtalkQrcodeFailed\": \"Не удалось получить QR-код DingTalk. Попробуйте ещё раз.\",\n \"dingtalkQrcodeExpired\": \"QR-код истёк. Получите новый.\",\n \"qqSetupGuide\": \"Отсканируйте QR-код с помощью QQ, чтобы авторизовать бота. После сканирования APP ID и Client Secret будут автоматически получены и введены в поле.\",\n \"qqScanAuth\": \"Отсканируйте штрихкод, чтобы авторизовать бота\",\n \"qqGetQrcode\": \"Получить QR-код QQ\",\n \"qqScanHint\": \"Отсканируйте QR-код выше с помощью QQ. После завершения авторизации APP ID и Client Secret будут автоматически введены в поле.\",\n \"qqAuthSuccess\": \"Авторизация бота QQ прошла успешно, APP ID и Client Secret уже введены в поле.\",\n \"qqQrcodeFailed\": \"Не удалось получить QR-код QQ. Попробуйте позже.\",\n \"qqQrcodeExpired\": \"Срок действия QR-кода истек. Пожалуйста, получите новый.\",\n \"atSenderOnReply\": \"@ отправителя при ответе\",\n \"atSenderOnReplyTooltip\": \"При включении бот будет упоминать отправителя в первом сообщении ответа в групповых чатах. Примечание: в режиме карточек @упоминание работает только для внутренних сотрудников организации.\",\n \"dingtalkEndpoint\": \"API Endpoint\",\n \"dingtalkEndpointTooltip\": \"Пользовательский API-адрес для приватного развёртывания DingTalk. Оставьте пустым для использования официального адреса по умолчанию.\",\n \"wecomCancelled\": \"Авторизация отменена\",\n \"wecomAuthFailed\": \"Ошибка авторизации: {{msg}}\",\n \"filterAll\": \"Все\",\n \"builtin\": \"Встроенные\",\n \"custom\": \"Пользовательские\",\n \"feishuRegion\": \"Регион\",\n \"feishuRegionTooltip\": \"Выберите Feishu для Китая или Lark для международного использования\",\n \"feishuChina\": \"Feishu (Китай)\",\n \"feishuInternational\": \"Lark (Международный)\",\n \"feishuScanGuide\": \"Отсканируйте QR-код в Feishu для мгновенного создания бота. Приложение Feishu будет создано автоматически, а App ID и App Secret будут заполнены.\",\n \"feishuScanLogin\": \"Сканировать для создания бота\",\n \"feishuGetQrcode\": \"Получить QR-код Feishu\",\n \"feishuScanHint\": \"Отсканируйте QR-код выше с помощью Feishu. App ID и App Secret будут заполнены автоматически после авторизации.\",\n \"feishuAuthSuccess\": \"Бот Feishu создан. App ID и App Secret заполнены автоматически.\",\n \"feishuQrcodeExpired\": \"QR-код истёк. Получите новый.\",\n \"feishuQrcodeFailed\": \"Не удалось получить QR-код Feishu. Попробуйте ещё раз.\",\n \"wechatSetupGuide\": \"Бот для личного аккаунта WeChat (протокол iLink). При первом запуске без Bot Token будет выведена ссылка на QR-код для авторизации. Токен сохраняется локально для дальнейшего использования.\",\n \"wechatContextTokenLimit\": \"Ограничение платформы WeChat iLink: context_token каждого сообщения пользователя позволяет отправить максимум 10 ответных сообщений. Это жёсткое ограничение платформы. Рекомендуется отключить вывод размышлений и инструментов или включить функцию объединения сообщений, чтобы избежать превышения лимита и ошибок отправки.\",\n \"wechatBotToken\": \"Bot Token\",\n \"wechatBotTokenTooltip\": \"Bearer-токен, полученный после входа по QR-коду. Оставьте пустым для запуска процедуры входа при старте.\",\n \"wechatBotTokenPlaceholder\": \"Заполняется автоматически после QR-входа или введите вручную\",\n \"wechatBotTokenFile\": \"Путь к файлу токена\",\n \"wechatBotTokenFileTooltip\": \"Путь для сохранения Bot Token. По умолчанию: ~/.qwenpaw/wechat_bot_token\",\n \"wechatMediaDir\": \"Директория медиафайлов\",\n \"wechatMessageMerge\": \"Объединение сообщений\",\n \"wechatMessageMergeTooltip\": \"Объединяет несколько ответных сообщений для отправки меньшим количеством, чтобы не превышать лимит WeChat в 10 сообщений на context_token. При задержке 0 все текстовые ответы объединяются в одно сообщение в конце запроса. Положительная задержка (в мс) объединяет соседние сообщения в указанном временном окне.\",\n \"wechatMessageMergeDelayMs\": \"Задержка объединения (мс)\",\n \"wechatMessageMergeDelayMsTooltip\": \"Временное окно для объединения соседних сообщений. Значение 0 объединяет все сообщения в один финальный ответ. Положительное значение (напр. 1000) буферизует сообщения в течение указанного периода перед отправкой.\",\n \"wechatMessageMergeDelayMsValidation\": \"Введите допустимое неотрицательное целое число\",\n \"wechatScanLogin\": \"Войти через QR-код\",\n \"wechatGetQrcode\": \"Получить QR-код для входа\",\n \"wechatScanHint\": \"Отсканируйте QR-код в WeChat, затем нажмите Сохранить\",\n \"wechatQrcodeFailed\": \"Не удалось получить QR-код, попробуйте снова\",\n \"wechatLoginSuccess\": \"Вход через WeChat выполнен, токен заполнен\",\n \"wechatQrcodeExpired\": \"QR-код истёк, получите новый\",\n \"shareSessionInGroup\": \"Общий контекст в группе\",\n \"shareSessionInGroupTooltip\": \"Если включено, все участники группы используют общий контекст разговора. Если выключено, у каждого участника независимый контекст.\",\n \"accessControlEnabled\": \"Контроль доступа\",\n \"accessControlEnabledTooltip\": \"При включении только пользователи из белого списка могут взаимодействовать с ботом. Неизвестным пользователям отображается их ID и они добавляются в список ожидания.\",\n \"accessControlDm\": \"Контроль доступа в ЛС\",\n \"accessControlDmTooltip\": \"При включении только пользователи из белого списка могут общаться с ботом в личных сообщениях\",\n \"accessControlGroup\": \"Контроль доступа в группах\",\n \"accessControlGroupTooltip\": \"При включении только пользователи из белого списка могут общаться с ботом в групповых чатах\",\n \"dmDisabled\": \"Отключить ЛС\",\n \"dmDisabledTooltip\": \"При включении бот будет полностью игнорировать все личные сообщения\",\n \"groupDisabled\": \"Отключить группы\",\n \"groupDisabledTooltip\": \"При включении бот будет полностью игнорировать все групповые сообщения\",\n \"manageAccessControl\": \"Контроль доступа\",\n \"pendingApprovals\": \"Ожидают одобрения\",\n \"whitelist\": \"Белый список\",\n \"blacklist\": \"Чёрный список\",\n \"addUser\": \"Добавить\",\n \"addUserPlaceholder\": \"Введите ID пользователя\",\n \"userId\": \"ID пользователя\",\n \"username\": \"Имя пользователя\",\n \"channel\": \"Канал\",\n \"firstMessage\": \"Первое сообщение\",\n \"time\": \"Время\",\n \"actions\": \"Действия\",\n \"approve\": \"Белый список\",\n \"deny\": \"Чёрный список\",\n \"dismiss\": \"Отклонить\",\n \"noWhitelistUsers\": \"Нет пользователей в белом списке\",\n \"noBlacklistUsers\": \"Нет пользователей в чёрном списке\",\n \"noPendingApprovals\": \"Нет ожидающих одобрения\",\n \"userAdded\": \"Пользователь добавлен\",\n \"userRemoved\": \"Пользователь удалён\",\n \"approveSuccess\": \"Добавлен в белый список\",\n \"denySuccess\": \"Добавлен в чёрный список\",\n \"dismissSuccess\": \"Отклонено\",\n \"operationFailed\": \"Операция не удалась\",\n \"remark\": \"Примечание\",\n \"usernamePlaceholder\": \"Введите имя пользователя (необязательно)\",\n \"remarkPlaceholder\": \"Введите примечание (необязательно)\",\n \"filterByChannel\": \"Фильтр по каналу\",\n \"selectToOperate\": \"Выберите элементы для операции\",\n \"selectedCount\": \"Выбрано: {{count}}\",\n \"batchApprove\": \"Одобрить\",\n \"batchDeny\": \"В чёрный список\",\n \"batchDismiss\": \"Удалить\",\n \"batchApproveConfirm\": \"Одобрить {{count}} пользователей?\",\n \"batchDenyConfirm\": \"Добавить {{count}} пользователей в чёрный список?\",\n \"batchDismissConfirm\": \"Удалить {{count}} пользователей?\",\n \"batchSuccess\": \"{{count}} пользователей обработано\",\n \"batchRemove\": \"Удалить\",\n \"batchRemoveConfirm\": \"Удалить {{count}} выбранных пользователей?\",\n \"enabledSection\": \"Активированные\",\n \"enabledCount\": \"{{count}}\",\n \"availableSection\": \"Доступные\",\n \"enableAction\": \"Активировать\",\n \"noEnabledChannels\": \"Нет активированных каналов\",\n \"goEnableChannels\": \"Активировать каналы\"\n },\n \"sessions\": {\n \"title\": \"Сессии\",\n \"description\": \"Просмотр и управление активными чат-сессиями\",\n \"loading\": \"Загрузка сессий...\",\n \"confirmDelete\": \"Подтвердить удаление\",\n \"deleteConfirm\": \"Вы уверены, что хотите удалить эту сессию?\",\n \"batchDeleteConfirm\": \"Вы уверены, что хотите удалить выбранные сессии ({{count}})?\",\n \"deleteSuccess\": \"Сессия успешно удалена\",\n \"deleteFailed\": \"Не удалось удалить сессию\",\n \"batchDeleteSuccess\": \"Успешно удалено сессий: {{count}}\",\n \"batchDeleteFailed\": \"Не удалось массово удалить сессии\",\n \"saveSuccess\": \"Сессия успешно сохранена\",\n \"saveFailed\": \"Не удалось сохранить сессию\",\n \"batchDeleteButton\": \"Массовое удаление\",\n \"filterTitle\": \"Фильтр по названию\",\n \"filterUserId\": \"Фильтр по ID пользователя\",\n \"filterChannel\": \"Фильтр по каналу\",\n \"allChannels\": \"Все каналы\",\n \"totalItems\": \"Всего {{count}} элементов\",\n \"selectedItems\": \"Выбрано {{count}} элементов\",\n \"editSession\": \"Редактировать сессию\",\n \"pleaseInputName\": \"Пожалуйста, введите название сессии\",\n \"sessionNamePlaceholder\": \"Название сессии\",\n \"activeTab\": \"Активные\",\n \"archivedTab\": \"Архив\",\n \"archivedAtColumn\": \"Дата архивации\",\n \"archive\": {\n \"action\": \"Архивировать\",\n \"unaction\": \"Разархивировать\",\n \"batchAction\": \"Архивировать выбранные\",\n \"batchUnaction\": \"Разархивировать выбранные\",\n \"success\": \"Чат архивирован\",\n \"successHint\": \"Чат архивирован. Смотрите в Панель управления > Сессии > Архив.\",\n \"failed\": \"Не удалось архивировать\",\n \"unarchiveSuccess\": \"Чат разархивирован\",\n \"unarchiveFailed\": \"Не удалось разархивировать\",\n \"batchSuccess\": \"{{count}} чат(ов) архивировано\",\n \"batchFailed\": \"Не удалось архивировать выбранные\",\n \"batchUnarchiveSuccess\": \"{{count}} чат(ов) разархивировано\",\n \"batchUnarchiveFailed\": \"Не удалось разархивировать выбранные\",\n \"groupLabel\": \"Архив ({{count}})\",\n \"modalTitle\": \"Архивированные чаты\",\n \"unarchiveAll\": \"Разархивировать все\",\n \"noArchived\": \"Нет архивированных чатов\"\n }\n },\n \"environments\": {\n \"parent\": \"Настройки\",\n \"environments\": \"Переменные окружения\",\n \"title\": \"Переменные окружения\",\n \"description\": \"Настройка переменных окружения (ключ-значение) для агентов и навыков.\",\n \"key\": \"Ключ\",\n \"value\": \"Значение\",\n \"variableNamePlaceholder\": \"VARIABLE_NAME\",\n \"valuePlaceholder\": \"value\",\n \"insertRowBelow\": \"Вставить строку ниже\",\n \"deleteRow\": \"Удалить строку\",\n \"deleteVariable\": \"Удалить переменную\",\n \"deleteConfirm\": \"Удалить «{{name}}»?\",\n \"deleteSelected\": \"Удалить выбранные\",\n \"deleteSelectedConfirm\": \"Удалить {{label}}?\",\n \"keyRequired\": \"Ключ обязателен\",\n \"invalidKeyFormat\": \"Неверный формат ключа\",\n \"duplicateKey\": \"Дубликат ключа\",\n \"saveSuccess\": \"Переменные окружения сохранены\",\n \"saveFailed\": \"Не удалось сохранить\",\n \"deleteSuccess\": \"«{{name}}» удалено\",\n \"deleteFailed\": \"Не удалось удалить\",\n \"noVariables\": \"Переменные окружения пока не настроены.\",\n \"addVariable\": \"Добавить переменную\",\n \"loading\": \"Загрузка…\",\n \"retry\": \"Повторить\",\n \"of\": \"из\",\n \"selected\": \"выбрано\",\n \"variable\": \"переменная\",\n \"variables\": \"переменных\",\n \"showValue\": \"Показать значение\",\n \"hideValue\": \"Скрыть значение\"\n },\n \"tokenUsage\": {\n \"title\": \"Использование токенов\",\n \"totalTokens\": \"Всего токенов\",\n \"totalCalls\": \"Всего вызовов\",\n \"promptTokens\": \"Входные токены\",\n \"completionTokens\": \"Выходные токены\",\n \"byModel\": \"По модели\",\n \"byDate\": \"По дате\",\n \"model\": \"Модель\",\n \"date\": \"Дата\",\n \"loadFailed\": \"Не удалось загрузить данные об использовании токенов\",\n \"noData\": \"Нет данных об использовании токенов за выбранный период\",\n \"modelTrend\": \"Тренд использования моделей\",\n \"tokenTypeChart\": \"Тренд типов токенов\",\n \"selectAll\": \"Выбрать все\",\n \"allSelected\": \"Все\",\n \"itemsSelected\": \"Выбрано: {{count}}\",\n \"selectModels\": \"Выберите модели\",\n \"selectTokenTypes\": \"Выберите типы токенов\"\n },\n \"agentStats\": {\n \"title\": \"Статистика агента\",\n \"description\": \"Просмотр статистики агента за выбранный период.\",\n \"totalSessions\": \"Всего сессий\",\n \"totalMessages\": \"Всего сообщений\",\n \"userMessages\": \"Сообщения пользователя\",\n \"assistantMessages\": \"Сообщения ассистента\",\n \"promptTokens\": \"Входные токены\",\n \"completionTokens\": \"Выходные токены\",\n \"llmCalls\": \"Вызовы LLM\",\n \"toolCalls\": \"Вызовы инструментов\",\n \"channel\": \"Канал\",\n \"sessionCount\": \"Сессий\",\n \"sessions\": \"Сессии\",\n \"newSessions\": \"Новые сессии\",\n \"activeSessions\": \"Активные сессии\",\n \"messages\": \"Сообщения\",\n \"messageTrend\": \"Динамика сообщений\",\n \"sessionTrend\": \"Динамика сессий\",\n \"tokenTrend\": \"Динамика токенов\",\n \"llmAndToolTrend\": \"Динамика вызовов LLM и инструментов\",\n \"sessionsByChannel\": \"Сессии по каналам\",\n \"messagesByChannel\": \"Сообщения по каналам\",\n \"totalSessionsTooltip\": \"Всего созданных чат-сессий. Каждая сессия — независимый контекст разговора\",\n \"totalMessagesTooltip\": \"Всего сообщений во всех сессиях, включая сообщения пользователя и ассистента\",\n \"promptTokensTooltip\": \"Всего отправленных LLM промпт-токенов\",\n \"completionTokensTooltip\": \"Всего сгенерированных LLM токенов завершения\",\n \"llmCallsTooltip\": \"Количество вызовов LLM API. Каждый запрос считается за один вызов\",\n \"toolCallsTooltip\": \"Количество решений агента вызвать инструмент. Один вызов LLM может породить несколько намерений использования инструментов\",\n \"messageTrendTooltip\": \"Сравнение сообщений пользователя и ассистента по дням\",\n \"sessionTrendTooltip\": \"Сравнение новых и активных сессий по дням. Оранжевые столбцы — созданные в этот день сессии, синие — сессии с сообщениями в этот день\",\n \"tokenTrendTooltip\": \"Динамика потребления входных и выходных токенов\",\n \"llmAndToolTrendTooltip\": \"Сравнение вызовов LLM и намерений использования инструментов по дням\",\n \"sessionsByChannelTooltip\": \"Распределение сессий по каналам. Сессии без канала по умолчанию группируются в Console\",\n \"messagesByChannelTooltip\": \"Распределение общего количества сообщений по каналам\",\n \"loadFailed\": \"Не удалось загрузить статистику\",\n \"noData\": \"Нет статистических данных за выбранный период\",\n \"retry\": \"Повторить\"\n },\n \"modelSelector\": {\n \"selectModel\": \"Выбрать модель\",\n \"noConfiguredModels\": \"Нет настроенных моделей\",\n \"noFreeModels\": \"Нет доступных бесплатных моделей\",\n \"switchFailed\": \"Не удалось переключить модель\",\n \"searchModels\": \"Поиск моделей...\",\n \"noModelsFound\": \"Подходящие модели не найдены\",\n \"free\": \"Бесплатно\",\n \"vision\": \"Видение\",\n \"freeBannerText\": \"Бесплатные модели могут иметь лимиты использования и работать нестабильно. Подробности уточняйте у провайдера.\",\n \"moreProviders\": \"Больше бесплатных провайдеров (нужен API Key)\",\n \"proBannerText\": \"Показаны модели, настроенные в разделе Настройки.\",\n \"connectToUse\": \"Подключить {{provider}} для бесплатных моделей\",\n \"configureApiKey\": \"Настроить API Key {{provider}} для бесплатных моделей\",\n \"configureApiKeyTitle\": \"Перейти к настройке\",\n \"configureApiKeyConfirm\": \"Вы будете перенаправлены на страницу моделей для настройки API Key {{provider}}. Продолжить?\",\n \"goToConfigure\": \"Перейти\",\n \"noModelsDiscovered\": \"Подключено. Загрузка моделей...\",\n \"oauthTitle\": \"Подключиться к {{provider}}\",\n \"oauthDescription\": \"Откроется новое окно браузера для авторизации доступа к {{provider}}. API-ключ будет сохранён автоматически.\",\n \"oauthContinue\": \"Продолжить\",\n \"oauthWaiting\": \"Ожидание авторизации...\",\n \"oauthWaitingDescription\": \"Пожалуйста, завершите авторизацию в окне браузера.\",\n \"oauthConnected\": \"Успешно подключено к {{provider}}\",\n \"oauthFailed\": \"Авторизация не удалась, попробуйте снова\",\n \"viewMore\": \"Показать ещё ({{count}})\"\n },\n \"models\": {\n \"llmConfiguration\": \"Конфигурация LLM\",\n \"providersTitle\": \"Провайдеры\",\n \"providersDescription\": \"Настройте API-ключи и endpoints для каждого провайдера.\",\n \"llmTitle\": \"Модели\",\n \"defaultLlm\": \"LLM по умолчанию\",\n \"llmDescription\": \"Здесь задается глобальная модель LLM по умолчанию у авторизованного провайдера. Для конкретного Agent модель можно выбрать отдельно на странице Chat.\",\n \"configureProvider\": \"Настроить {{name}}\",\n \"baseURL\": \"Базовый URL\",\n \"apiKey\": \"API-ключ\",\n \"advancedConfig\": \"Расширенная конфигурация\",\n \"authMode\": \"Режим аутентификации\",\n \"authModeApiKey\": \"API Key (x-api-key)\",\n \"authModeAuthToken\": \"Auth Token (Authorization: Bearer)\",\n \"customHeaders\": \"Пользовательские заголовки\",\n \"customHeadersHint\": \"Добавляются к каждому запросу API, например x-app: cli\",\n \"customHeaderKey\": \"Название заголовка\",\n \"customHeaderValue\": \"Значение заголовка\",\n \"addHeader\": \"+ Добавить заголовок\",\n \"generateConfig\": \"Параметры генерации\",\n \"generateConfigHint\": \"Параметры генерации в формате JSON будут развёрнуты и переданы в запрос на генерацию (`openai.chat.completions` или `anthropic.messages`).\",\n \"generateConfigInvalidJson\": \"Пожалуйста, введите корректный JSON\",\n \"generateConfigMustBeObject\": \"Параметры генерации должны быть JSON-объектом\",\n \"modelAdvancedConfig\": \"Расширенная конфигурация модели\",\n \"modelGenerateConfig\": \"Параметры генерации модели\",\n \"modelGenerateConfigHint\": \"Параметры генерации в формате JSON для этой модели. Они будут развёрнуты и переданы в запрос на генерацию. Имеют приоритет над расширенной конфигурацией провайдера.\",\n \"modelConfigSaved\": \"Конфигурация модели {{name}} сохранена\",\n \"modelConfigSaveFailed\": \"Не удалось сохранить конфигурацию модели\",\n \"modelConfigLabel\": \"Конфигурация модели\",\n \"maxTokensLabel\": \"Макс. токенов\",\n \"maxTokensHint\": \"Максимальное количество выходных токенов на ответ\",\n \"maxInputLengthLabel\": \"Макс. длина контекста\",\n \"maxInputLengthHint\": \"Размер контекстного окна модели, управляет порогом сжатия контекста (≥1000)\",\n \"relayReasoningLabel\": \"Передавать рассуждения\",\n \"relayReasoningHint\": \"Передавать содержимое рассуждений в последующие ходы диалога\",\n \"thinkingModeLabel\": \"Включить мышление\",\n \"thinkingModeHint\": \"Включите для активации мышления/рассуждения. Выключите для отключения мышления.\",\n \"thinkingBudgetLabel\": \"Бюджет мышления\",\n \"thinkingBudgetHint\": \"Используется бюджет мышления по умолчанию\",\n \"switchToManual\": \"Вручную\",\n \"switchToAuto\": \"Авто\",\n \"reasoningEffortLabel\": \"Глубина рассуждения\",\n \"reasoningEffortHint\": \"Управляет глубиной рассуждения (семейство OpenAI)\",\n \"protocol\": \"Протокол\",\n \"protocolHint\": \"Выберите протокол API провайдера для этой конфигурации.\",\n \"selectProtocol\": \"Пожалуйста, выберите протокол\",\n \"protocolOpenAI\": \"OpenAI-совместимый (Chat Completions)\",\n \"protocolOpenAIResponse\": \"OpenAI-совместимый (Response API)\",\n \"protocolAnthropic\": \"Anthropic (Messages API)\",\n \"currentKey\": \"Текущий: {{key}}\",\n \"startsWith\": \"Начинается с «{{prefix}}»\",\n \"optionalSelfHosted\": \"Необязательно для self-hosted сервисов\",\n \"leaveBlankKeep\": \"Оставьте пустым, чтобы сохранить текущий ключ\",\n \"enterApiKey\": \"Введите API-ключ ({{prefix}}...)\",\n \"enterApiKeyOptional\": \"Введите API-ключ (необязательно)\",\n \"openAIEndpoint\": \"OpenAI endpoint, например https://api.openai.com/v1\",\n \"openAICompatibleEndpoint\": \"OpenAI-совместимый endpoint, например https://api.example.com (добавляйте /v1 только если ваш провайдер этого требует)\",\n \"azureEndpointHint\": \"Azure OpenAI endpoint, например https://<resource>.openai.azure.com/openai/v1\",\n \"anthropicEndpointHint\": \"Anthropic endpoint, например https://api.anthropic.com\",\n \"ollamaEndpointHint\": \"Ollama endpoint, например http://localhost:11434\",\n \"lmstudioEndpointHint\": \"LM Studio endpoint, например http://localhost:1234/v1\",\n \"apiEndpointHint\": \"API endpoint, например https://api.example.com\",\n \"pleaseEnterBaseURL\": \"Пожалуйста, введите базовый URL API\",\n \"selectBaseURL\": \"Выберите базовый URL\",\n \"selectBaseURLHint\": \"Выберите региональную конечную точку этого провайдера\",\n \"pleaseEnterValidURL\": \"Пожалуйста, введите корректный URL\",\n \"apiKeyShouldStart\": \"API-ключ должен начинаться с «{{prefix}}»\",\n \"configurationSaved\": \"Конфигурация {{name}} сохранена\",\n \"failedToSaveConfig\": \"Не удалось сохранить конфигурацию\",\n \"revokeAuthorization\": \"Отозвать авторизацию\",\n \"revokeConfirmContent\": \"Вы уверены, что хотите удалить API-ключ для {{name}}? Текущая конфигурация LLM-модели также будет очищена.\",\n \"revokeConfirmSimple\": \"Вы уверены, что хотите удалить API-ключ для {{name}}?\",\n \"authorizationRevoked\": \"Авторизация {{name}} отозвана, LLM-модель очищена\",\n \"authorizationRevokedSimple\": \"Авторизация {{name}} отозвана\",\n \"failedToRevoke\": \"Не удалось отозвать авторизацию\",\n \"active\": \"Активно: {{provider}} / {{model}}\",\n \"provider\": \"Провайдер\",\n \"model\": \"Модель\",\n \"selectProvider\": \"Выберите провайдера (должен быть авторизован)\",\n \"selectModel\": \"Выберите модель\",\n \"llmModelUpdated\": \"LLM-модель обновлена\",\n \"failedToSave\": \"Не удалось сохранить\",\n \"freeModelWarningTitle\": \"Уведомление о бесплатной модели\",\n \"freeModelWarningMessage\": \"Бесплатные модели могут работать нестабильно. Подробности смотрите в условиях провайдера:\",\n \"freeModelWarningDontShowAgain\": \"Больше не показывать\",\n \"saved\": \"Сохранено\",\n \"save\": \"Сохранить\",\n \"cancel\": \"Отмена\",\n \"loading\": \"Загрузка...\",\n \"retry\": \"Повторить\",\n \"notSet\": \"Не задано\",\n \"notRequired\": \"Не требуется\",\n \"available\": \"Доступно\",\n \"unavailable\": \"Недоступно\",\n \"providerAvailable\": \"Готово (есть модели)\",\n \"providerNoModels\": \"Не готово (нет моделей)\",\n \"search\": \"Поиск\",\n \"searchPlaceholder\": \"Поиск провайдеров...\",\n \"searchModelPlaceholder\": \"Поиск моделей...\",\n \"providerNotConfigured\": \"Не готово (не настроено)\",\n \"settings\": \"Настройки\",\n \"actions\": \"Действия\",\n \"custom\": \"Пользовательский\",\n \"builtin\": \"Встроенный\",\n \"freeTierGroup\": \"Бесплатный уровень\",\n \"paidGroup\": \"Платные\",\n \"localCustomGroup\": \"Локальные и пользовательские\",\n \"endpointCount\": \"{{count}} конечных точек\",\n \"connect\": \"OAuth\",\n \"saveApiKey\": \"Сохранить\",\n \"addProvider\": \"Добавить провайдера\",\n \"addProviderTitle\": \"Добавить пользовательского провайдера\",\n \"providerIdLabel\": \"ID провайдера\",\n \"providerIdPlaceholder\": \"например: openai, google, anthropic\",\n \"providerIdHint\": \"Строчные буквы, цифры, дефисы и подчёркивания. Нельзя изменить позже.\",\n \"providerNameLabel\": \"Отображаемое имя\",\n \"providerNamePlaceholder\": \"например: OpenAI, Google Gemini\",\n \"defaultBaseUrlLabel\": \"Базовый URL по умолчанию\",\n \"defaultBaseUrlPlaceholder\": \"например: https://api.example.com\",\n \"apiKeyPrefixLabel\": \"Префикс API-ключа (необязательно)\",\n \"apiKeyPrefixPlaceholder\": \"например: sk-\",\n \"providerCreated\": \"Провайдер «{{name}}» создан\",\n \"providerCreateFailed\": \"Не удалось создать провайдера\",\n \"deleteProvider\": \"Удалить провайдера\",\n \"deleteProviderConfirm\": \"Удалить пользовательского провайдера «{{name}}» и все его модели? Это действие нельзя отменить.\",\n \"providerDeleted\": \"Провайдер «{{name}}» удалён\",\n \"providerDeleteFailed\": \"Не удалось удалить провайдера\",\n \"manageModels\": \"Модели\",\n \"models\": \"Модели\",\n \"manageModelsTitle\": \"{{provider}} — Управление моделями\",\n \"userAdded\": \"Добавлено пользователем\",\n \"addModel\": \"Добавить модель\",\n \"addModels\": \"Добавить модели\",\n \"autoDiscoverModels\": \"Автообнаружение моделей\",\n \"addModelTitle\": \"Добавить модель в {{provider}}\",\n \"modelIdLabel\": \"ID модели\",\n \"modelIdPlaceholder\": \"например: gpt-4o, gemini-2.0-flash\",\n \"modelDiscoveryUnavailableHint\": \"Доступные модели не найдены. Возможно, этот провайдер не поддерживает получение списка моделей, поэтому введите ID модели вручную.\",\n \"modelNameLabel\": \"Название модели\",\n \"modelNameRequired\": \"Пожалуйста, введите название модели\",\n \"modelNamePlaceholder\": \"например: GPT-4o, Gemini 2.0 Flash\",\n \"modelAdded\": \"Модель «{{name}}» добавлена\",\n \"modelAddFailed\": \"Не удалось добавить модель\",\n \"modelAlreadyExists\": \"Модель с ID \\\"{{id}}\\\" уже существует\",\n \"autoDiscoverModelsSuccess\": \"Автообнаружение завершено. Добавлено моделей: {{count}}\",\n \"autoDiscoverModelsNoNew\": \"Автообнаружение завершено. Новых моделей не найдено\",\n \"autoDiscoverModelsFailed\": \"Не удалось автоматически обнаружить модели\",\n \"removeModel\": \"Удалить\",\n \"removeModelConfirm\": \"Удалить модель «{{name}}» из {{provider}}?\",\n \"modelRemoved\": \"Модель «{{name}}» удалена\",\n \"modelRemoveFailed\": \"Не удалось удалить модель\",\n \"loadMore\": \"Загрузить ещё ({{count}})\",\n \"modelsCount\": \"{{count}} моделей\",\n \"noModels\": \"Нет моделей\",\n \"addModelFirst\": \"Сначала добавьте модель\",\n \"modelList\": \"Список моделей\",\n \"clearAll\": \"Очистить всё\",\n \"filteredModelsLoaded\": \"Загружено отфильтрованных моделей: {{count}}\",\n \"filterFailed\": \"Не удалось отфильтровать модели\",\n \"clearAllModels\": \"Очистить все модели\",\n \"clearAllModelsConfirm\": \"Удалить все дополнительные модели (всего {{count}})? Это действие нельзя отменить.\",\n \"allModelsCleared\": \"Число удаленных моделей: {{count}}\",\n \"filterModels\": \"Фильтровать модели\",\n \"filterByProvider\": \"Провайдер:\",\n \"searchProviderPlaceholder\": \"Поиск по названию провайдера\",\n \"selectAllProviders\": \"Выбрать все\",\n \"clearProviderSelection\": \"Снять все\",\n \"noMatchingProviders\": \"Нет подходящих провайдеров\",\n \"filterByModality\": \"Модальность ввода:\",\n \"filterFreeOnly\": \"Только бесплатные модели:\",\n \"getModels\": \"Получить модели\",\n \"discovered\": \"Доступные модели:\",\n \"add\": \"Добавить\",\n \"local\": \"Локальные\",\n \"localType\": \"Тип\",\n \"localEmbedded\": \"Встроенная (в процессе)\",\n \"localDownloadFirst\": \"Сервис модели не запущен\",\n \"localDownloadModel\": \"Скачать модель\",\n \"localModelsTitle\": \"{{provider}} — Локальные модели\",\n \"localNoModels\": \"Пока нет скачанных моделей\",\n \"localRepoId\": \"ID репозитория модели\",\n \"localRepoIdRequired\": \"Пожалуйста, введите ID репозитория модели\",\n \"localRepoIdPlaceholder\": \"например: Qwen/Qwen3-0.6B-GGUF\",\n \"localFilename\": \"Имя файла (необязательно)\",\n \"localFilenamePlaceholder\": \"например: mistral-7b.Q4_K_M.gguf\",\n \"localFilenameHint\": \"Оставьте пустым для автоматического выбора лучшей квантизации\",\n \"localSource\": \"Источник\",\n \"localDownloadSuccess\": \"Модель успешно скачана\",\n \"localDownloadFailed\": \"Не удалось скачать модель\",\n \"localDeleteModel\": \"Удалить модель\",\n \"localDeleteConfirm\": \"Удалить локальную модель «{{name}}»? Файл модели будет удалён с диска.\",\n \"localModelDeleted\": \"Модель «{{name}}» удалена\",\n \"localDeleteFailed\": \"Не удалось удалить модель\",\n \"localDownloadPending\": \"Подготовка к загрузке...\",\n \"localDownloading\": \"Загрузка {{repo}}... Это может занять несколько минут.\",\n \"localDownloadNavigateHint\": \"Вы можете перейти на другую страницу — загрузка продолжится в фоне.\",\n \"localDownloadInProgress\": \"Загрузка уже выполняется\",\n \"localDownloaded\": \"Загружено\",\n \"localNotDownloaded\": \"Не загружено\",\n \"localNoRecommendedModels\": \"Текущая конфигурация устройства слишком слабая для запуска локальных моделей\",\n \"localCancelDownloadTitle\": \"Отменить загрузку\",\n \"localCancelDownloadAction\": \"Отменить загрузку\",\n \"localCancelDownloadConfirm\": \"Отменить загрузку «{{repo}}»?\",\n \"localDownloadCancelled\": \"Загрузка отменена\",\n \"localCancelDownloadFailed\": \"Не удалось отменить загрузку\",\n \"localLlamacppName\": \"Движок инференса\",\n \"localLlamacppInstallHint\": \"Сначала установите локальную среду, затем можно скачивать или запускать модель.\",\n \"localInstallLlamacpp\": \"Установить llama.cpp\",\n \"localLlamacppInstallStarted\": \"Загрузка llama.cpp началась\",\n \"localLlamacppInstallSuccess\": \"llama.cpp успешно установлен\",\n \"localLlamacppInstallFailed\": \"Не удалось установить llama.cpp\",\n \"localRuntimeSectionTitle\": \"Inference Engine\",\n \"localRuntimeSectionDescription\": \"Powered by Llama.cpp\",\n \"localRuntimeComputeHint\": \"По умолчанию используется CPU. Если нужна GPU-акселерация, используйте Ollama или LM Studio.\",\n \"localRuntimeInstalled\": \"Установлен\",\n \"localRuntimeUpdateAvailable\": \"Доступно обновление\",\n \"localRuntimeUpdateAction\": \"Нажмите, чтобы скачать последнюю версию\",\n \"localRuntimeUpdateConfirmTitle\": \"Подтвердите обновление llama.cpp\",\n \"localRuntimeUpdateConfirmContent\": \"Обновление перезапишет текущую установленную версию llama.cpp. Подтвердите, чтобы начать загрузку и установку последней версии.\",\n \"localRuntimeUpdateConfirmContentWithServer\": \"Обновление перезапишет текущую установленную версию llama.cpp и остановит запущенный сервис модели ({{model}}). Подтвердите, чтобы начать загрузку и установку последней версии.\",\n \"localRuntimeUnsupported\": \"Не поддерживается\",\n \"localRuntimeMissing\": \"Не установлен\",\n \"localServerRunningTitle\": \"Текущий локальный сервер\",\n \"localServerOnline\": \"Работает\",\n \"localServerIdle\": \"Недоступен\",\n \"localServerUnavailable\": \"Ошибка\",\n \"localNoActiveModel\": \"Нет активной модели\",\n \"localNoDownloadedModelsHint\": \"Сначала скачайте подходящую модель, а затем запускайте локальный сервис инференса.\",\n \"localDownloadModelFirst\": \"Сначала скачайте модель\",\n \"localEngineInstallStateLabel\": \"Установка\",\n \"localEngineRunStateLabel\": \"Статус\",\n \"localEngineCurrentModelLabel\": \"Текущая обслуживаемая модель\",\n \"localEngineNextStepLabel\": \"Следующий шаг\",\n \"localEngineHealthLabel\": \"Состояние движка\",\n \"localEngineStatusHint\": \"Скачайте и запустите подходящую модель из списка ниже\",\n \"localStartServer\": \"Запустить\",\n \"localStopServer\": \"Остановить\",\n \"localServerStartFailed\": \"Не удалось запустить локальный сервер\",\n \"localServerStopFailed\": \"Не удалось остановить локальный сервер\",\n \"localServerSwitchTitle\": \"Переключить запущенную модель\",\n \"localServerSwitchConfirm\": \"Сейчас запущена модель {{current}}. Переключить на {{next}}?\",\n \"localSwitchModel\": \"Переключить\",\n \"localModelPathMissing\": \"Для этой загруженной модели отсутствует локальный путь.\",\n \"localModelsSectionTitle\": \"Локальные модели\",\n \"localModelsSectionDescription\": \"Загруженные модели можно запускать сразу. Одновременно может выполняться только одна загрузка модели и один локальный сервер.\",\n \"localModelsLockedHint\": \"Сначала установите llama.cpp\",\n \"localAlternativeRuntimeHint\": \"Попробуйте использовать Ollama или LM Studio\",\n \"localModelsCount\": \"Загружено {{downloaded}} / {{total}}\",\n \"localCurrentDownloadTitle\": \"Текущая загрузка\",\n \"localAdvancedConfigTitle\": \"Расширенные настройки локальной модели\",\n \"localAdvancedConfigDescription\": \"Эти настройки применяются к встроенному локальному рантайму инференса. Изменения максимальной длины контекста вступят в силу при следующем запуске локального сервера.\",\n \"localAdvancedConfigSaved\": \"Расширенные настройки локальной модели сохранены\",\n \"localAdvancedConfigSaveFailed\": \"Не удалось сохранить расширенные настройки локальной модели\",\n \"localMaxContextLengthLabel\": \"Макс. длина контекста\",\n \"localMaxContextLengthHint\": \"Максимальное число токенов в контекстном окне модели, определяющее, сколько истории помещается в каждый запрос. Изменения вступят в силу при следующем запуске локального сервера.\",\n \"localMaxContextLengthRequired\": \"Укажите макс. длину контекста\",\n \"localMaxContextLengthMin\": \"Макс. длина контекста должна быть не меньше {{min}}\",\n \"localMaxContextLengthPlaceholder\": \"Введите макс. длину контекста\",\n \"localServerPortLabel\": \"Порт сервера\",\n \"localServerPortHint\": \"Оставьте поле пустым, чтобы llama.cpp автоматически выбрал свободный порт при запуске. Укажите порт, если хотите использовать фиксированный порт при следующих запусках. Изменения вступят в силу при следующем запуске локального сервера.\",\n \"localServerPortRange\": \"Порт сервера должен быть в диапазоне от {{min}} до {{max}}\",\n \"localServerPortPlaceholder\": \"Оставьте пустым для автовыбора порта\",\n \"localGenerateConfigHint\": \"Настройте параметры генерации локальной модели в виде JSON-объекта. Они будут развёрнуты в последующие запросы генерации.\",\n \"localCustomModelTitle\": \"Загрузка пользовательской модели\",\n \"localCustomModelHint\": \"Введите ID репозитория модели. Поддерживается только GGUF.\",\n \"localSourceHuggingFace\": \"Hugging Face\",\n \"localSourceModelScope\": \"ModelScope\",\n \"ollamaModelNamePlaceholder\": \"например: mistral:7b, qwen3:8b\",\n \"testConnection\": \"Проверить подключение\",\n \"testConnectionSuccess\": \"Проверка подключения прошла успешно\",\n \"testConnectionFailed\": \"Проверка подключения не удалась\",\n \"testConnectionFailedWithMessage\": \"Проверка подключения не удалась: {{message}}\",\n \"testConnectionError\": \"Произошла ошибка при проверке подключения\",\n \"discoverModels\": \"Обнаружить модели\",\n \"discoverModelsFailed\": \"Не удалось обнаружить модели\",\n \"ollamaFetchModelsFailed\": \"Не удалось получить список моделей Ollama. Проверьте подробности ошибки.\",\n \"modelTestFailed\": \"Проверка модели не пройдена, проверьте корректность ID модели\",\n \"modelTestFailedConfirm\": \"Тест подключения модели не пройден: {{message}}. Всё равно добавить эту модель?\",\n \"autoDiscoveredAndAdded\": \"Автоматически обнаружено {{count}} моделей и добавлено {{added}} новых\",\n \"autoDiscoveredNoNew\": \"Автоматически обнаружено {{count}} моделей; список моделей уже актуален\",\n \"autoDiscoverFailed\": \"Автоматическое обнаружение моделей не удалось; вы можете добавить модели вручную\",\n \"tagImage\": \"Изображение\",\n \"tagVideo\": \"Видео\",\n \"tagTextOnly\": \"Только текст\",\n \"tagMultimodal\": \"Мультимодальность\",\n \"tagVision\": \"Зрение\",\n \"tagText\": \"Текст\",\n \"free\": \"Бесплатно\",\n \"tagNotProbed\": \"Не проверено\",\n \"probeMultimodal\": \"Тест мультимодальности\",\n \"probeImage\": \"Изображение\",\n \"probeVideo\": \"Видео\",\n \"probeSupported\": \"Поддерживается: {{types}}\",\n \"probeNotSupported\": \"Эта модель не поддерживает мультимодальный ввод\",\n \"probeFailed\": \"Ошибка проверки\",\n \"modalityVision\": \"Изображение\",\n \"modalityAudio\": \"Аудио\",\n \"modalityVideo\": \"Видео\",\n \"modalityFile\": \"Файл\",\n \"modalityText\": \"Только текст\",\n \"perMillionIn\": \"/1M вх.\",\n \"perMillionOut\": \"/1M исх.\"\n },\n \"agentConfig\": {\n \"title\": \"Конфигурация\",\n \"description\": \"Настройка параметров выполнения агента\",\n \"reactAgentTitle\": \"Агент ReAct\",\n \"llmRetryTitle\": \"Автоповтор LLM\",\n \"llmRetryEnabled\": \"Включить автоповтор\",\n \"llmRetryEnabledTooltip\": \"Автоматически повторять временные ошибки LLM API, такие как лимиты, таймауты и проблемы соединения. Применяется к новым запросам после сохранения.\",\n \"llmMaxRetries\": \"Макс. число повторов\",\n \"llmMaxRetriesTooltip\": \"Максимальное количество дополнительных повторов при временной ошибке LLM.\",\n \"llmMaxRetriesPlaceholder\": \"Введите макс. число повторов\",\n \"llmMaxRetriesRequired\": \"Требуется указать макс. число повторов\",\n \"llmMaxRetriesMin\": \"Макс. число повторов должно быть не меньше 1\",\n \"llmBackoffBase\": \"Базовая задержка backoff (с)\",\n \"llmBackoffBaseTooltip\": \"Базовая задержка перед первой повторной попыткой. Дальше используется экспоненциальный backoff.\",\n \"llmBackoffBasePlaceholder\": \"Введите базовую задержку\",\n \"llmBackoffBaseRequired\": \"Требуется указать базовую задержку backoff\",\n \"llmBackoffBaseMin\": \"Базовая задержка backoff должна быть не меньше 0.1 секунды\",\n \"llmBackoffCap\": \"Макс. задержка backoff (с)\",\n \"llmBackoffCapTooltip\": \"Максимальный предел задержки для экспоненциального backoff.\",\n \"llmBackoffCapPlaceholder\": \"Введите макс. задержку\",\n \"llmBackoffCapRequired\": \"Требуется указать макс. задержку backoff\",\n \"llmBackoffCapMin\": \"Макс. задержка backoff должна быть не меньше 0.5 секунды\",\n \"llmBackoffCapGteBase\": \"Макс. задержка backoff должна быть не меньше базовой задержки\",\n \"llmRateLimiterTitle\": \"Ограничитель скорости LLM\",\n \"llmMaxConcurrent\": \"Макс. одновременных запросов\",\n \"llmMaxConcurrentTooltip\": \"Максимальное число одновременно выполняемых LLM-запросов. Общее для всех агентов; вступает в силу при первой инициализации и после перезапуска.\",\n \"llmMaxConcurrentPlaceholder\": \"Введите макс. число одновременных запросов\",\n \"llmMaxConcurrentRequired\": \"Требуется указать макс. число одновременных запросов\",\n \"llmMaxConcurrentRange\": \"Макс. число одновременных запросов должно быть не менее 1\",\n \"llmMaxQpm\": \"Макс. запросов в минуту (QPM)\",\n \"llmMaxQpmTooltip\": \"Максимальное число запросов в 60-секундном скользящем окне. Запросы, превышающие лимит, ожидают перед отправкой — это предотвращает ошибки 429. 0 = без ограничений.\",\n \"llmMaxQpmPlaceholder\": \"Введите макс. QPM (0 = без ограничений)\",\n \"llmMaxQpmRequired\": \"Требуется указать макс. QPM\",\n \"llmMaxQpmRange\": \"Макс. QPM должен быть не менее 0\",\n \"llmRateLimitPause\": \"Пауза при лимите (с)\",\n \"llmRateLimitPauseTooltip\": \"Длительность паузы (в секундах) при получении ответа 429. Переопределяется заголовком Retry-After от API.\",\n \"llmRateLimitPausePlaceholder\": \"Введите длительность паузы\",\n \"llmRateLimitPauseRequired\": \"Требуется указать паузу при лимите\",\n \"llmRateLimitPauseMin\": \"Пауза при лимите должна быть не менее 1 секунды\",\n \"llmRateLimitJitter\": \"Диапазон джиттера (с)\",\n \"llmRateLimitJitterTooltip\": \"Случайный диапазон джиттера (в секундах), добавляемый к паузе для разбивки одновременных пробуждений.\",\n \"llmRateLimitJitterPlaceholder\": \"Введите диапазон джиттера\",\n \"llmRateLimitJitterRequired\": \"Требуется указать диапазон джиттера\",\n \"llmRateLimitJitterMin\": \"Диапазон джиттера должен быть не менее 0 секунд\",\n \"llmAcquireTimeout\": \"Таймаут получения слота (с)\",\n \"llmAcquireTimeoutTooltip\": \"Максимальное время ожидания слота ограничителя скорости перед ошибкой.\",\n \"llmAcquireTimeoutPlaceholder\": \"Введите таймаут получения слота\",\n \"llmAcquireTimeoutRequired\": \"Требуется указать таймаут получения слота\",\n \"llmAcquireTimeoutMin\": \"Таймаут получения слота должен быть не менее 10 секунд\",\n \"llmAcquireTimeoutGtPauseJitter\": \"Таймаут получения слота должен быть больше суммы паузы и джиттера\",\n \"contextManagementTitle\": \"Управление контекстом\",\n \"memoryManagerBackend\": \"Бэкенд управления долговременной памятью\",\n \"memoryManagerBackendTooltip\": \"Тип бэкенда менеджера памяти. Выберите remelight (локальные файлы) или adbpg (облачная база данных).\",\n \"memoryManagerBackendRestartWarning\": \"Смена бэкенда управления долговременной памятью не поддерживает горячую перезагрузку. Сохраните и перезапустите QwenPaw для применения изменений.\",\n \"maxIters\": \"Макс. итераций\",\n \"maxItersTooltip\": \"Максимальное число итераций рассуждение-действие для агента ReAct\",\n \"maxItersPlaceholder\": \"Введите макс. число итераций\",\n \"maxItersRequired\": \"Требуется указать макс. число итераций\",\n \"maxItersMin\": \"Макс. число итераций должно быть не меньше 1\",\n \"agentLoopTitle\": \"Настройки Loop агента\",\n \"loopMode\": {\n \"builtIn\": \"Встроенный\",\n \"builtInNote\": \"Порядок заблокирован · Значения доступны\",\n \"templateSectionTitle\": \"Шаблоны Loop\",\n \"templateSectionDescription\": \"Выберите встроенный шаблон или добавьте свой.\",\n \"defaultTab\": \"По умолчанию\",\n \"goalTab\": \"Цель\",\n \"missionTab\": \"Миссия\",\n \"defaultDescription\": \"Стандартный защищённый ReAct Loop вне явного режима.\",\n \"goalDescription\": \"Ограниченный постоянный Loop для проверяемых целей.\",\n \"missionDescription\": \"Постоянный процесс для длительных многоэтапных миссий.\",\n \"gatePipeline\": \"Цепочка Gate\",\n \"repetitionTitle\": \"Защита от повторов\",\n \"repetitionDescription\": \"Обнаруживает повторные вызовы инструментов и вмешивается.\",\n \"iterationTitle\": \"Лимит итераций\",\n \"iterationDescription\": \"Ограничивает число итераций ReAct.\",\n \"retryTitle\": \"Качественная проверка завершения\",\n \"retryDescription\": \"Срабатывает только для текстового ответа без вызовов инструментов.\",\n \"goalTurnTitle\": \"Лимит ходов цели\",\n \"goalTurnDescription\": \"Ограничивает ходы внутри активной цели.\",\n \"maxGoalTurns\": \"Максимум ходов цели\",\n \"goalBudgetTitle\": \"Бюджет токенов цели\",\n \"goalBudgetDescription\": \"Останавливает цель при достижении бюджета.\",\n \"maxGoalTokens\": \"Максимум токенов цели\",\n \"goalCompletionTitle\": \"Проверка завершения цели\",\n \"goalCompletionDescription\": \"Читает явный статус цели перед остановкой.\",\n \"goalCompletionReadOnly\": \"Завершение контролируется встроенным протоколом цели.\",\n \"missionProgressTitle\": \"Проверка прогресса миссии\",\n \"missionProgressDescription\": \"Продолжает до успеха всех историй или лимита.\",\n \"missionProgressReadOnly\": \"Завершение определяется встроенным статусом Mission Story.\",\n \"missionIterationTitle\": \"Лимит итераций Mission\",\n \"missionIterationDescription\": \"Задайте стандартный лимит выполнения для новых Mission.\",\n \"workerAttemptsTitle\": \"Попытки Worker\",\n \"workerAttemptsDescription\": \"Ограничьте повторы Worker после неудачной проверки Story.\",\n \"missionVerificationTitle\": \"Политика проверки Mission\",\n \"missionVerificationDescription\": \"Задайте стандартные инструкции проверки и тестовую команду.\",\n \"defaultMaxMissionIterations\": \"Максимум раундов выполнения по умолчанию\",\n \"defaultMaxMissionIterationsTooltip\": \"Используется, если /mission не задаёт --max-iterations.\",\n \"maxRetriesPerStory\": \"Максимум повторов для Story\",\n \"maxRetriesPerStoryTooltip\": \"Повторы Worker после результата FAIL или PARTIAL от Verifier.\",\n \"defaultVerificationInstructions\": \"Инструкции по проверке (необязательно)\",\n \"defaultVerificationInstructionsHelp\": \"Инструкции на естественном языке для каждого Mission Verifier.\",\n \"defaultVerificationInstructionsPlaceholder\": \"Например: проверьте пути Windows и фактический интерфейс.\",\n \"defaultVerifyCommand\": \"Тестовая команда по умолчанию (необязательно)\",\n \"defaultVerifyCommandHelp\": \"Verifier запускает её обычными инструментами и с обычными разрешениями. /mission --verify переопределяет значение.\",\n \"defaultVerifyCommandPlaceholder\": \"Например: pytest -q\",\n \"enableRepetitionProtection\": \"Включить защиту от повторов\",\n \"removeRule\": \"Удалить правило\",\n \"custom\": \"Пользовательский\",\n \"customDescription\": \"Создайте сохраняемую цепочку из встроенных Gate QwenPaw.\",\n \"duplicate\": \"Дублировать\",\n \"delete\": \"Удалить\",\n \"displayName\": \"Отображаемое имя\",\n \"duplicateModeName\": \"Режим Loop с таким именем уже существует.\",\n \"slashCommand\": \"Slash-команда\",\n \"description\": \"Описание\",\n \"availableToAgent\": \"Доступен этому агенту\",\n \"runsTopToBottom\": \"Выполняется сверху вниз\",\n \"addGate\": \"Добавить Gate\",\n \"emptyPipeline\": \"Добавьте хотя бы один Gate, чтобы режим стал доступен.\",\n \"iterationGateTitle\": \"Лимит итераций\",\n \"iterationGateDescription\": \"Остановка после заданного числа итераций Loop.\",\n \"doomGateTitle\": \"Защита от повторов\",\n \"doomGateDescription\": \"Обнаруживает повторные вызовы и меняет стратегию.\",\n \"tokenGateTitle\": \"Бюджет токенов\",\n \"tokenGateDescription\": \"Ограничивает токены запроса и ответа.\",\n \"timeoutGateTitle\": \"Лимит времени Loop\",\n \"timeoutGateDescription\": \"Остановка на следующей границе Loop без прерывания текущей операции.\",\n \"toolBudgetGateTitle\": \"Бюджет вызовов инструментов\",\n \"toolBudgetGateDescription\": \"Ограничивает все и выбранные вызовы.\",\n \"retryGateTitle\": \"Проверка ранней остановки\",\n \"retryGateDescription\": \"Просит агента проверить результат перед завершением.\",\n \"rubricGateTitle\": \"Критерии завершения\",\n \"rubricGateDescription\": \"Оценивает явные критерии и запускает исправление.\",\n \"qualitativeRubricTitle\": \"Качественная проверка завершения\",\n \"qualitativeRubricDescription\": \"Проверяет текстовые ответы без вызовов инструментов по критериям на естественном языке.\",\n \"qualitativeRubric\": \"Критерии завершения на естественном языке\",\n \"qualitativeRubricHelp\": \"Срабатывает только для текстовых ответов без вызовов инструментов и продолжает работу, если она не завершена.\",\n \"completionRubricTitle\": \"Проверка сигнала завершения\",\n \"completionRubricDescription\": \"Проверяет сигнал завершения в текстовом ответе без вызовов инструментов.\",\n \"completionRubricHelp\": \"Срабатывает после текстовых ответов без вызовов инструментов. По завершении агент выводит сигнал; иначе продолжает работу до достижения максимального числа проверок.\",\n \"completionPrompt\": \"Промпт проверки завершения\",\n \"completionPromptHelp\": \"Опишите, когда результат считается завершённым. QwenPaw автоматически добавит инструкцию вывода сигнала завершения.\",\n \"completionSignal\": \"Сигнал завершения\",\n \"completionSignalHelp\": \"QwenPaw добавляет этот сигнал и требование вывести только его в промпт проверки. Loop завершается только при полном совпадении без учёта регистра и крайних пробелов.\",\n \"removeCriterion\": \"Удалить критерий {{number}}\",\n \"totalTokensHelp\": \"Максимум входных и выходных токенов вместе за этот ход.\",\n \"promptTokensHelp\": \"Включает системные инструкции, контекст, схемы инструментов и их результаты.\",\n \"completionTokensHelp\": \"Максимум токенов, создаваемых моделью за этот ход.\",\n \"maxIterations\": \"Максимум итераций\",\n \"historyWindow\": \"Окно истории\",\n \"similarityThreshold\": \"Порог сходства\",\n \"totalTokens\": \"Максимум всех токенов\",\n \"promptTokens\": \"Максимум входных токенов\",\n \"completionTokens\": \"Максимум выходных токенов\",\n \"maxSeconds\": \"Максимум секунд\",\n \"maxSecondsHelp\": \"Время проверяется после завершения текущего ответа модели или вызова инструмента. Выполняемая операция не прерывается.\",\n \"allToolCalls\": \"Все вызовы инструментов\",\n \"perToolLimits\": \"Лимиты по инструментам\",\n \"toolName\": \"Имя инструмента\",\n \"callLimitAria\": \"Лимит вызовов {{name}}\",\n \"removeToolLimitAria\": \"Удалить лимит {{name}}\",\n \"addToolLimit\": \"Добавить лимит инструмента\",\n \"maxRetries\": \"Максимум повторов\",\n \"retryInstruction\": \"Инструкция повтора\",\n \"passThreshold\": \"Порог прохождения\",\n \"maxEvaluations\": \"Максимум раундов оценки\",\n \"graderError\": \"Ошибка оценщика\",\n \"stopSafely\": \"Безопасно остановить\",\n \"verifyOnceMore\": \"Проверить ещё раз\",\n \"completionCriteria\": \"Критерии завершения\",\n \"criterionPlaceholder\": \"Опишите наблюдаемый результат\",\n \"required\": \"Обязательно\",\n \"weight\": \"Вес\",\n \"addCriterion\": \"Добавить критерий\",\n \"moveGateAria\": \"Переместить {{title}}\",\n \"removeGate\": \"Удалить Gate\",\n \"newModeName\": \"Новый режим Loop\",\n \"defaultCustomDescription\": \"Пользовательская цепочка Gate.\",\n \"copyName\": \"{{name}} — копия\",\n \"createModeAria\": \"Создать режим Loop\",\n \"createModeTitle\": \"Создать пользовательский режим Loop\",\n \"startingTemplate\": \"Начальный шаблон\",\n \"startingPipeline\": \"Начальный конвейер\",\n \"blankPipelineHint\": \"Начните с пустого режима и добавьте гейты позже\",\n \"safeTemplate\": \"Безопасный запуск\",\n \"researchTemplate\": \"Исследование с бюджетом\",\n \"qualityTemplate\": \"Приоритет качества\",\n \"blankTemplate\": \"Пустая цепочка\"\n },\n \"doomLoopEnabled\": \"Защита от повторений\",\n \"doomLoopEnabledTooltip\": \"Автоматическое вмешательство, когда агент застревает на повторении одних и тех же действий\",\n \"doomLoopAfter\": \"После\",\n \"doomLoopRepetitions\": \"одинаковых действий\",\n \"doomLoopStopAction\": \"Пауза и запрос помощи\",\n \"doomLoopWarnAction\": \"Отправить напоминание\",\n \"doomLoopSimpleMode\": \"Простой\",\n \"doomLoopAdvancedMode\": \"Расширенный\",\n \"doomLoopWindowSize\": \"Диапазон обнаружения\",\n \"doomLoopWindowSizeTooltip\": \"Сколько последних действий проверять на повторение\",\n \"doomLoopSimilarity\": \"Чувствительность совпадения\",\n \"doomLoopSimilarityTooltip\": \"Насколько похожими должны быть действия для обнаружения повтора (ниже = строже)\",\n \"doomLoopStages\": \"Правила вмешательства\",\n \"doomLoopAction\": \"Действие\",\n \"doomLoopPrompt\": \"Сообщение\",\n \"doomLoopPromptPlaceholder\": \"Текст напоминания или причина паузы...\",\n \"doomLoopAddStage\": \"Добавить правило\",\n \"iterationTitle\": \"Лимит итераций\",\n \"iterationEnabled\": \"Включить лимит итераций\",\n \"iterationEnabledTooltip\": \"Остановить агента после фиксированного числа циклов\",\n \"iterationMaxIterations\": \"Максимум итераций\",\n \"iterationMaxIterationsTooltip\": \"Максимальное число циклов до остановки\",\n \"rubricTitle\": \"Качественная проверка завершения\",\n \"rubricEnabled\": \"Включить качественную проверку завершения\",\n \"rubricEnabledTooltip\": \"Срабатывает только для текстового ответа без вызовов инструментов.\",\n \"rubricPrompt\": \"Критерии завершения на естественном языке\",\n \"rubricPromptTooltip\": \"Промпт для запроса оценки завершённости задачи\",\n \"rubricDesc\": \"Для текстового ответа без вызовов инструментов применяет критерии завершения на естественном языке и просит продолжить, если работа осталась.\",\n \"rubricPromptPlaceholder\": \"Вы не вызвали ни одного инструмента. Если задача завершена, подтвердите. В противном случае продолжайте с вызовами инструментов.\",\n \"rubricMaxInterventions\": \"Максимум раундов оценки\",\n \"rubricMaxInterventionsTooltip\": \"Максимальное число проверок завершения текстовых ответов без вызовов инструментов за один ход.\",\n \"inLoopModes\": \"Включить в режимах /goal и /mission\",\n \"inLoopModesTooltip\": \"Также применять, когда активен режим /goal или /mission\",\n \"comingSoonEditable\": \"Пользовательская настройка будет доступна в будущем выпуске.\",\n \"simpleMode\": \"Простой\",\n \"advancedMode\": \"Расширенный\",\n \"comingSoon\": \"Скоро\",\n \"reactModeTab\": \"Шаблон Loop - По умолчанию\",\n \"goalModeTab\": \"Шаблон Loop - Goal\",\n \"missionModeTab\": \"Шаблон Loop - Mission\",\n \"goalModeInfoTitle\": \"Режим Goal vs По умолчанию\",\n \"goalModeInfo\": \"Режим по умолчанию останавливается после ответа. Режим Goal заставляет агента циклически работать над целью, используя Rubric-оценку для определения завершения. Все операции выполняются в текущем контексте агента.\",\n \"missionModeInfoTitle\": \"Режим Mission vs Goal\",\n \"missionModeInfo\": \"Режим Mission автоматически разбивает сложные задачи на подзадачи, выполняемые независимыми Worker-агентами и проверяемые Verifier-агентами. Каждая подзадача выполняется в собственном контексте, не загрязняя историю основной сессии.\",\n \"goalIterationGate\": \"Шлюз итераций цели\",\n \"goalIterationGateDesc\": \"Ограничивает количество ходов агента в сессии цели, отслеживая итерации и использование токенов.\",\n \"goalBudgetGate\": \"Шлюз бюджета токенов\",\n \"goalBudgetGateDesc\": \"Устанавливает лимит расхода токенов для сессии цели. Останавливает агента при превышении бюджета.\",\n \"goalRubricGate\": \"Рубрика завершения цели\",\n \"goalRubricGateDesc\": \"Оценивает, достигнута ли цель, проверяя статус сессии.\",\n \"goalDoomGate\": \"Защита от повторений\",\n \"goalDoomGateDesc\": \"Обнаруживает повторяющиеся паттерны во время выполнения цели и инициирует вмешательство.\",\n \"missionProgressGate\": \"Шлюз прогресса миссии\",\n \"missionProgressGateDesc\": \"Отслеживает завершение пользовательских историй PRD. Продолжает до прохождения всех историй или достижения максимума итераций.\",\n \"missionIterBypass\": \"Обход итераций\",\n \"missionIterBypassDesc\": \"Временно снимает ограничение итераций ReAct во время выполнения миссии для длительных фаз.\",\n \"customLoopTitle\": \"Пользовательские режимы цикла\",\n \"customLoopDesc\": \"Создавайте свои режимы цикла с пользовательскими комбинациями шлюзов. Скоро.\",\n \"autoContinueOnTextOnly\": \"Автопродолжение при только тексте\",\n \"autoContinueOnTextOnlyTooltip\": \"Если шаг рассуждения завершился только текстом без вызова инструментов, вставляется подсказка и выполняется ещё один проход с той же политикой инструментов, чтобы реже обрывались многошаговые задачи. Выключите для прежнего поведения. При включении возможен рост расхода токенов.\",\n \"autoGenerateSessionTitle\": \"Автогенерация заголовков сессий\",\n \"autoGenerateSessionTitleTooltip\": \"После первого пользовательского сообщения в новом чате фоновая задача делает короткий LLM-вызов и заменяет усечённую заглушку на лаконичный заголовок. На каждый новый чат приходится один дополнительный короткий LLM-вызов. Отключите, чтобы оставить заглушку и не тратить токены.\",\n \"shellCommandTimeout\": \"Тайм-аут shell-команды (с)\",\n \"shellCommandTimeoutTooltip\": \"Тайм-аут по умолчанию (в секундах) для инструмента execute_shell_command. LLM может переопределить это значение для каждого вызова через параметр timeout. Полезно для длительных задач, таких как компиляция или обработка данных.\",\n \"shellCommandTimeoutPlaceholder\": \"Введите тайм-аут\",\n \"shellCommandTimeoutRequired\": \"Тайм-аут shell-команды обязателен\",\n \"shellCommandTimeoutMin\": \"Тайм-аут shell-команды должен быть не менее 1 секунды\",\n \"shellCommandExecutable\": \"Исполняемый файл shell\",\n \"shellCommandExecutableTooltip\": \"Путь к shell, используемому execute_shell_command. Linux/macOS: например, /bin/bash, /bin/zsh. Windows: поддерживаются powershell.exe, pwsh.exe или POSIX-совместимые shell (например, Git Bash). Если пусто, используется переменная окружения $SHELL, затем платформенный shell по умолчанию (/bin/sh для Unix, cmd.exe для Windows).\",\n \"shellCommandExecutablePlaceholder\": \"напр. /bin/bash или powershell.exe (пусто = автоопределение)\",\n \"maxInputLength\": \"Макс. длина входа\",\n \"maxInputLengthTooltip\": \"Максимальная длина входа (токены) для окна контекста модели\",\n \"maxInputLengthPlaceholder\": \"Введите макс. длину входа\",\n \"maxInputLengthRequired\": \"Требуется указать макс. длину входа\",\n \"maxInputLengthMin\": \"Макс. длина входа должна быть не меньше 1000\",\n \"contextCompactRatio\": \"Коэффициент сжатия контекста\",\n \"contextCompactRatioTooltip\": \"Доля контекста для сжатия при заполнении\",\n \"contextCompactRatioRequired\": \"Требуется указать коэффициент сжатия контекста\",\n \"contextCompactThreshold\": \"Порог сжатия контекста\",\n \"contextCompactThresholdTooltip\": \"Порог сжатия контекста (токены), автоматически вычисляется из макс. длины входа и коэффициента сжатия\",\n \"contextCompactThresholdPlaceholder\": \"Автоматически\",\n \"contextCompactReserveRatio\": \"Коэффициент резерва контекста\",\n \"contextCompactReserveRatioTooltip\": \"Доля контекста для резерва при сжатии\",\n \"contextCompactReserveRatioRequired\": \"Требуется указать коэффициент резерва контекста\",\n \"contextCompactReserveThreshold\": \"Порог резерва контекста\",\n \"contextCompactReserveThresholdTooltip\": \"Порог резерва контекста (токены), автоматически вычисляется из макс. длины входа и коэффициента резерва\",\n \"contextCompactReserveThresholdPlaceholder\": \"Автоматически\",\n \"toolResultCompactRecentN\": \"recent_n результатов инструментов\",\n \"toolResultCompactRecentNTooltip\": \"Количество результатов инструментов для использования порога недавних сообщений\",\n \"toolResultCompactRecentNRequired\": \"recent_n результатов инструментов обязательно\",\n \"toolResultCompactOldThreshold\": \"Макс. символов для результатов инструментов за пределами recent_n\",\n \"toolResultCompactOldThresholdTooltip\": \"Результаты инструментов за пределами recent_n, превышающие этот лимит, будут сжаты\",\n \"toolResultCompactOldThresholdPlaceholder\": \"Введите порог символов\",\n \"toolResultCompactOldThresholdRequired\": \"Макс. символов для результатов за пределами recent_n обязательно\",\n \"toolResultCompactRecentThreshold\": \"Макс. символов для результатов инструментов в пределах recent_n\",\n \"toolResultCompactRecentThresholdTooltip\": \"Результаты инструментов в пределах recent_n, превышающие этот лимит, будут сжаты. Также задаёт максимальное количество байт, возвращаемых за один вызов read_file. Должно быть больше или равно порогу для старых сообщений\",\n \"toolResultCompactRecentThresholdPlaceholder\": \"Введите порог символов\",\n \"toolResultCompactRecentThresholdRequired\": \"Макс. символов для результатов в пределах recent_n обязательно\",\n \"toolResultCompactRetentionDays\": \"Дни хранения файлов результатов инструментов\",\n \"toolResultCompactRetentionDaysTooltip\": \"Количество дней хранения сжатых файлов, просроченные файлы удаляются автоматически\",\n \"toolResultCompactRetentionDaysRequired\": \"Дни хранения файлов результатов инструментов обязательны\",\n \"historyRetentionDays\": \"Дни хранения истории\",\n \"historyRetentionDaysTooltip\": \"Количество дней хранения постоянной истории чата. Более старые записи автоматически удаляются при запуске и завершении работы агента.\",\n \"historyRetentionDaysRequired\": \"Дни хранения истории обязательны\",\n \"historyRetentionDaysForeverWarning\": \"Значение 0 хранит историю вечно — объём хранилища будет расти без ограничений.\",\n \"historyRetentionDaysLargeWarning\": \"Большой период хранения может занимать значительный объём хранилища.\",\n \"exemptFileExtensions\": \"Исключенные расширения файлов\",\n \"exemptFileExtensionsTooltip\": \"Результаты read_file для этих расширений будут использовать порог для новых сообщений\",\n \"exemptFileExtensionsPlaceholder\": \".md, .txt, .json\",\n \"exemptToolNames\": \"Исключенные инструменты\",\n \"exemptToolNamesTooltip\": \"Результаты этих инструментов будут использовать порог для новых сообщений\",\n \"exemptToolNamesPlaceholder\": \"chat_with_agent, list_agents\",\n \"language\": \"Язык агента\",\n \"languageTooltip\": \"Язык файлов персонажа агента (SOUL.md, AGENTS.md и др.). При смене языка MD-файлы будут скопированы на выбранном языке.\",\n \"languageConfirmTitle\": \"Смена языка агента\",\n \"languageConfirmContent\": \"При смене языка следующие файлы будут перезаписаны версией на новом языке:\\n\\n SOUL.md, AGENTS.md, PROFILE.md, MEMORY.md, BOOTSTRAP.md, HEARTBEAT.md\\n\\nЕсли вы вносили изменения в эти файлы, сначала сделайте резервную копию. Остальные файлы не будут затронуты.\",\n \"languageConfirmOk\": \"Сменить язык\",\n \"languageSaveSuccess\": \"Язык успешно обновлён\",\n \"languageSaveSuccessWithFiles\": \"Язык обновлён, скопировано файлов: {{count}}\",\n \"languageSaveFailed\": \"Не удалось обновить язык\",\n \"timezone\": \"Часовой пояс пользователя\",\n \"timezoneTooltip\": \"Используется для запланированных задач, отображения времени и контекста агента. По умолчанию — системный часовой пояс.\",\n \"timezoneRequired\": \"Пожалуйста, выберите часовой пояс\",\n \"selectTimezone\": \"Выберите часовой пояс\",\n \"timezoneSaveSuccess\": \"Часовой пояс обновлён\",\n \"timezoneSaveFailed\": \"Не удалось обновить часовой пояс\",\n \"saveSuccess\": \"Конфигурация успешно сохранена\",\n \"saveFailed\": \"Не удалось сохранить конфигурацию\",\n \"loadFailed\": \"Не удалось загрузить конфигурацию\",\n \"historyMaxLength\": \"Макс. длина истории\",\n \"historyMaxLengthTooltip\": \"Максимальная длина вывода команды /history (в символах)\",\n \"historyMaxLengthRequired\": \"Требуется указать макс. длину истории\",\n \"historyMaxLengthMin\": \"Макс. длина истории должна быть не меньше 1000\",\n \"historyMaxLengthPlaceholder\": \"Введите макс. длину\",\n \"contextCompactTitle\": \"Настройки сжатия контекста\",\n \"contextCompactEnabled\": \"Включить сжатие контекста\",\n \"contextCompactEnabledTooltip\": \"Автоматически сжимать контекст для предотвращения переполнения окна контекста\",\n \"tokenCountModel\": \"Модель для подсчёта токенов\",\n \"tokenCountModelTooltip\": \"Имя модели для точного подсчёта токенов; установите \\\"default\\\" для использования встроенной оценки\",\n \"tokenCountModelPlaceholder\": \"Имя модели или default\",\n \"tokenCountUseMirror\": \"Использовать зеркало HuggingFace\",\n \"tokenCountUseMirrorTooltip\": \"Использовать зеркальный сайт HuggingFace при загрузке токенизаторов\",\n \"tokenCountEstimateDivisor\": \"Делитель оценки byte/Token\",\n \"tokenCountEstimateDivisorTooltip\": \"Делитель для оценки токенов на основе байтов (токены ≈ байты / делитель). Рекомендуемое значение: 3.75–4.\",\n \"tokenCountEstimateDivisorRequired\": \"Требуется указать делитель оценки\",\n \"tokenCountEstimateDivisorMin\": \"Делитель оценки должен быть от 2 до 5\",\n \"toolResultCompactTitle\": \"Настройки сжатия результатов инструментов\",\n \"toolResultCompactEnabled\": \"Включить сжатие результатов инструментов\",\n \"toolResultCompactEnabledTooltip\": \"Сжимать слишком длинные результаты вызовов инструментов для экономии пространства контекста\",\n \"memorySummaryTitle\": \"Настройки долгосрочной памяти\",\n \"memorySummaryEnabled\": \"Запустить сводку памяти\",\n \"memorySummaryEnabledTooltip\": \"Автоматически генерировать и сохранять сводки памяти при сжатии контекста\",\n \"dreamCronEnabled\": \"Включить сновидения\",\n \"dreamCronEnabledTooltip\": \"Включить плановое задание оптимизации памяти через сновидения\",\n \"dreamCron\": \"Расписание сновидений\",\n \"dreamCronTooltip\": \"Cron-выражение для периодической оптимизации долгосрочных воспоминаний через состояние сновидения; используйте переключатель выше для включения или отключения\",\n \"dreamCronPlaceholder\": \"0 23 * * *\",\n \"dreamCronRequired\": \"Введите Cron-выражение, если сновидения включены\",\n \"dreamCronInvalid\": \"Введите допустимое Cron-выражение из 5 полей\",\n \"autoMemorySearch\": \"Автоматический поиск по памяти (Beta)\",\n \"autoMemorySearchTooltip\": \"Автоматически запускать поиск по памяти при каждом диалоге. Каждый запрос будет автоматически вызывать функцию поиска по памяти, помогая QwenPaw лучше понять вас — но это увеличивает расход токенов.\",\n \"autoMaxResults\": \"Макс. результатов автоматического поиска\",\n \"autoMaxResultsTooltip\": \"Максимальное количество результатов при включённом автоматическом поиске по памяти\",\n \"autoMaxResultsRequired\": \"Требуется указать макс. число результатов\",\n \"autoMaxResultsMin\": \"Макс. число результатов должно быть не меньше 1\",\n \"inboxPushEnabled\": \"Уведомления о результатах памяти\",\n \"inboxPushEnabledTooltip\": \"Отправлять результаты автоматической памяти, сновидений и ресурсов во входящие. Отключение не останавливает задачи памяти.\",\n \"autoMemoryInterval\": \"Интервал автоматической памяти\",\n \"autoMemoryIntervalTooltip\": \"Запускать автоматическую память каждые N пользовательских запросов. 0 отключает периодическую автоматическую память. 1 означает запуск после каждого запроса. Рекомендуемое значение: 2-5; при значении 1 учитывайте высокий расход токенов.\",\n \"autoMemoryIntervalPlaceholder\": \"Введите 0, чтобы отключить периодическую автоматическую память\",\n \"autoMemoryIntervalRequired\": \"Требуется указать интервал автоматической памяти\",\n \"autoMemoryIntervalMin\": \"Интервал автоматической памяти должен быть не меньше 0\",\n \"embeddingConfigTitle\": \"Настройки модели эмбеддинга\",\n \"embeddingBackend\": \"Тип SDK эмбеддинга\",\n \"embeddingBackendTooltip\": \"Тип SDK для вызова модели эмбеддинга\",\n \"embeddingBackendPlaceholder\": \"Выберите тип SDK\",\n \"embeddingApiKey\": \"API Key\",\n \"embeddingApiKeyTooltip\": \"API-ключ провайдера эмбеддинга; оставьте пустым, если аутентификация не требуется\",\n \"embeddingApiKeyPlaceholder\": \"Оставьте пустым, если не требуется\",\n \"embeddingBaseUrl\": \"Base URL\",\n \"embeddingBaseUrlTooltip\": \"Base URL API эмбеддинга. Для OpenAI-совместимых бэкендов этот пользовательский endpoint необязателен; API Key и название модели обязательны.\",\n \"embeddingBaseUrlPlaceholder\": \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n \"embeddingHost\": \"Host\",\n \"embeddingHostTooltip\": \"Адрес сервиса Ollama. Если оставить пустым, будет использован хост Ollama по умолчанию.\",\n \"embeddingHostPlaceholder\": \"http://localhost:11434\",\n \"embeddingModelName\": \"Название модели\",\n \"embeddingModelNameTooltip\": \"Название модели эмбеддинга. Обязательно для всех бэкендов эмбеддинга.\",\n \"embeddingModelNamePlaceholder\": \"text-embedding-v4\",\n \"embeddingEnableCache\": \"Включить кэш эмбеддинга\",\n \"embeddingEnableCacheTooltip\": \"Кэшировать результаты эмбеддинга для уменьшения повторных вычислений\",\n \"embeddingMaxCacheSize\": \"Макс. размер кэша\",\n \"embeddingMaxCacheSizeTooltip\": \"Максимальное количество записей в кэше эмбеддинга\",\n \"embeddingMaxCacheSizeRequired\": \"Требуется указать макс. размер кэша\",\n \"embeddingUseDimensions\": \"Использовать пользовательские размерности\",\n \"embeddingUseDimensionsTooltip\": \"Использовать пользовательские размерности эмбеддинга (поддерживается только некоторыми моделями)\",\n \"embeddingDimensions\": \"Размерности эмбеддинга\",\n \"embeddingDimensionsTooltip\": \"Количество размерностей эмбеддинга; вступает в силу при включённых пользовательских размерностях\",\n \"embeddingDimensionsRequired\": \"Требуется указать размерности эмбеддинга\",\n \"embeddingDimensionsMin\": \"Размерности должны быть не меньше 1\",\n \"embeddingMaxInputLength\": \"Макс. длина входа\",\n \"embeddingMaxInputLengthTooltip\": \"Максимальная длина входа (токены) для одного запроса эмбеддинга\",\n \"embeddingMaxInputLengthRequired\": \"Требуется указать макс. длину входа\",\n \"embeddingMaxBatchSize\": \"Макс. размер пакета\",\n \"embeddingMaxBatchSizeTooltip\": \"Максимальное количество элементов в одном пакетном запросе эмбеддинга\",\n \"embeddingMaxBatchSizeRequired\": \"Требуется указать макс. размер пакета\",\n \"embeddingRestartWarning\": \"Настройки модели эмбеддинга не поддерживают горячую перезагрузку. Сохраните и перезапустите сервис для применения изменений.\",\n \"maxContextLength\": \"Макс. длина контекста\",\n \"maxContextLengthTooltip\": \"Максимальное количество токенов в окне контекста модели, определяющее, сколько истории помещается в каждый запрос\",\n \"maxContextLengthRequired\": \"Требуется указать макс. длину контекста\",\n \"maxContextLengthMin\": \"Макс. длина контекста должна быть не меньше 1000\",\n \"maxContextLengthPlaceholder\": \"Введите макс. длину контекста\",\n \"embeddingActivationNote\": \"Эмбеддинг активируется, когда заполнены обязательные поля выбранного бэкенда.\",\n \"embeddingEnableHint\": \"Эмбеддинг активируется, когда заполнены обязательные поля: название модели и API Key для OpenAI-совместимых или Gemini бэкендов; название модели для Ollama.\",\n \"rebuildMemoryIndex\": \"Перестроить индекс памяти\",\n \"rebuildMemoryIndexWarning\": \"Эта операция очищает и перестраивает поисковый индекс памяти и может потреблять много ресурсов CPU и оперативной памяти. Используйте её только для восстановления повреждённого индекса или исправления некорректных результатов поиска.\",\n \"rebuildMemoryIndexConfirmTitle\": \"Перестроить индекс памяти?\",\n \"rebuildMemoryIndexConfirm\": \"Во время перестроения нагрузка на CPU и потребление оперативной памяти могут значительно возрасти. Операция не рекомендуется, если индекс не повреждён и не нуждается в восстановлении.\",\n \"rebuildMemoryIndexSuccess\": \"Индекс памяти успешно перестроен\",\n \"rebuildMemoryIndexFailed\": \"Не удалось перестроить индекс памяти: {{error}}\",\n \"toolResultCompactRecentGtOld\": \"Порог для последних сообщений должен быть больше или равен порогу для старых сообщений\",\n \"lightContextTitle\": \"Управление контекстом\",\n \"visualCompactCollapseLabel\": \"Визуальное сжатие\",\n \"visualCompactDescription\": \"Сжимает старый и длинный контекст в визуальные страницы, сокращая входные токены. Недавний диалог остаётся текстом.\",\n \"visualCompactEnabled\": \"Включить Visual Compact\",\n \"visualCompactEffort\": \"Интенсивность сжатия\",\n \"visualCompactLow\": \"Низкая\",\n \"visualCompactMedium\": \"Средняя\",\n \"visualCompactHigh\": \"Высокая\",\n \"visualCompactLowDescription\": \"Сжимает меньше содержимого и отдаёт приоритет читаемости.\",\n \"visualCompactMediumDescription\": \"Балансирует экономию токенов и визуальную читаемость.\",\n \"visualCompactHighDescription\": \"Сжимает больше подходящего контекста и отдаёт приоритет экономии токенов.\",\n \"visualCompactQualityNote\": \"Более сильное сжатие не обязательно повышает качество ответа.\",\n \"visualCompactCapabilityNote\": \"Работает только с моделями, поддерживающими ввод изображений.\",\n \"remeLightMemoryTitle\": \"Долгосрочная память\",\n \"toolExecutionLevelTitle\": \"Безопасность выполнения инструментов\",\n \"toolExecutionLevel\": {\n \"title\": \"Безопасность выполнения инструментов\",\n \"alertMessage\": \"Настройте политику одобрения вызовов инструментов для контроля уровня безопасности при выполнении инструментов агентом\",\n \"strict\": \"Строгий режим\",\n \"strictDesc\": \"Все вызовы инструментов требуют одобрения, максимальный уровень безопасности\",\n \"smart\": \"Умный режим\",\n \"smartDesc\": \"Инструменты с низким риском одобряются автоматически, средний+ риск требует одобрения\",\n \"auto\": \"Автоматический режим\",\n \"autoDesc\": \"Только инструменты, явно отмеченные для одобрения, требуют одобрения (по умолчанию)\",\n \"off\": \"Выключено\",\n \"offDesc\": \"Отключить все одобрения инструментов, все инструменты выполняются автоматически\"\n },\n \"saveLevelSuccess\": \"Уровень безопасности выполнения инструментов сохранён\",\n \"saveLevelFailed\": \"Не удалось сохранить уровень безопасности выполнения инструментов\",\n \"adbpgMemoryTitle\": \"ADBPG Long-term Memory\",\n \"adbpgConfig\": {\n \"title\": \"ADBPG Memory Configuration\",\n \"restBaseUrl\": \"REST Base URL\",\n \"restApiKey\": \"REST API Key\",\n \"memoryIsolation\": \"Memory Isolation (per-agent)\",\n \"searchTimeout\": \"Search Timeout\"\n }\n },\n \"tools\": {\n \"title\": \"Встроенные инструменты\",\n \"description\": \"Управляйте встроенными инструментами и их статусом. Отключённые инструменты не будут доступны агенту.\",\n \"emptyState\": \"Нет настроенных инструментов\",\n \"loadError\": \"Не удалось загрузить инструменты\",\n \"enableSuccess\": \"Инструмент успешно включён\",\n \"disableSuccess\": \"Инструмент успешно отключён\",\n \"toggleError\": \"Не удалось изменить статус инструмента\",\n \"enableAll\": \"Включить все\",\n \"disableAll\": \"Отключить все\",\n \"enableAllSuccess\": \"Все инструменты включены\",\n \"disableAllSuccess\": \"Все инструменты отключены\",\n \"allEnabled\": \"Все инструменты уже включены\",\n \"allDisabled\": \"Все инструменты уже отключены\",\n \"asyncExecution\": \"Асинхронное выполнение\",\n \"asyncExecutionEnabled\": \"Асинхронное выполнение включено\",\n \"asyncExecutionDisabled\": \"Асинхронное выполнение отключено\",\n \"browserUnifiedMode\": \"Унифицированный браузер (бета)\",\n \"browserLegacyMode\": \"Режим совместимости с устаревшей версией\",\n \"browserUnifiedModeButton\": \"Новый (бета)\",\n \"browserLegacyModeButton\": \"Старый (совм.)\",\n \"browserUnifiedDescription\": \"Использовать новые возможности унифицированного браузера.\",\n \"browserLegacyDescription\": \"Использовать исходную реализацию браузера для прежнего поведения.\",\n \"browserRestartPending\": \"После перезапуска служба переключится на: {{mode}}\",\n \"configure\": \"Настроить\",\n \"configured\": \"Настроено\",\n \"requiresConfig\": \"Требуется настройка\",\n \"configSaved\": \"Конфигурация сохранена\",\n \"configSaveError\": \"Не удалось сохранить конфигурацию\",\n \"active\": \"активно\",\n \"available\": \"Доступно\",\n \"noEnabled\": \"Нет включённых инструментов\",\n \"goEnableBtn\": \"Включить\",\n \"configureAction\": \"Настроить →\",\n \"enableAction\": \"Включить →\"\n },\n \"modelConfig\": {\n \"promptTitle\": \"Требуется LLM-модель — выберите в Chat после настройки\",\n \"promptMessage\": \"Для работы чата нужна LLM-модель. Сначала настройте модель, затем выберите её в селекторе моделей вверху страницы Chat.\",\n \"configureButton\": \"Настроить модель\",\n \"skipButton\": \"Пропустить\",\n \"chatDisabledTitle\": \"Чат отключён\",\n \"chatDisabledMessage\": \"Функции чата отключены, потому что не настроена модель. Настройте модель, чтобы включить чат.\",\n \"configureNow\": \"Настроить модель\",\n \"modelNotConfigured\": \"Перед использованием чата настройте модель в разделе «Настройки»\"\n },\n \"header\": {\n \"changelog\": \"Список изменений\",\n \"docs\": \"Документация\",\n \"tutorial\": \"Руководство\",\n \"featureDemos\": \"Демонстрация функций\",\n \"faq\": \"FAQ\",\n \"github\": \"GitHub\",\n \"resources\": \"Документация\",\n \"updateAvailable\": \"Доступно обновление\"\n },\n \"theme\": {\n \"dark\": \"Тёмная\",\n \"light\": \"Светлая\",\n \"system\": \"Как в системе\"\n },\n \"sidebar\": {\n \"newVersion\": \"Доступна новая версия: v{{version}}, нажмите для обновления\",\n \"updateModal\": {\n \"title\": \"Доступна новая версия: v{{version}}\",\n \"viewReleases\": \"Открыть релизы\",\n \"installDesktopUpdate\": \"Обновить сейчас\",\n \"desktopInstallHint\": \"QwenPaw Desktop {{version}} готов к установке. Приложение перезапустится после обновления.\",\n \"checking\": \"Проверка обновлений…\",\n \"checkingHint\": \"Проверяем последнюю версию\",\n \"downloading\": \"Загрузка обновления\",\n \"downloadingTo\": \"Обновление до версии {{version}}\",\n \"downloadProgress\": \"{{done}} / {{total}} · {{rate}}\",\n \"installing\": \"Установка обновления\",\n \"willRestart\": \"Приложение перезапустится автоматически после установки.\",\n \"stepPrepare\": \"Подготовка\",\n \"stepDownloading\": \"Загрузка\",\n \"stepInstalling\": \"Установка\",\n \"failedTitle\": \"Обновление не удалось\",\n \"back\": \"Назад\",\n \"retry\": \"Повторить\",\n \"errors\": {\n \"network\": \"Не удалось подключиться к серверу обновлений. Проверьте интернет и повторите попытку.\",\n \"signature\": \"Не удалось проверить подпись файла обновления. Попробуйте загрузить ещё раз.\",\n \"appLocation\": \"QwenPaw запущен из macOS-пути только для чтения. Переместите QwenPaw.app в папку Applications, запустите его оттуда и повторите обновление.\",\n \"other\": \"Обновление не удалось.\"\n },\n \"updateLater\": \"Скачать в фоне\",\n \"backgroundDownloading\": \"Загрузка в фоновом режиме…\",\n \"readyToInstall\": \"Обновление готово\",\n \"readyToInstallHint\": \"Обновление v{{version}} загружено. Перезапустите приложение, чтобы установить и открыть новую версию.\",\n \"restartNow\": \"Обновить сейчас\",\n \"backgroundFailed\": \"Фоновая загрузка не удалась\"\n },\n \"settings\": {\n \"language\": \"Язык\",\n \"theme\": \"Тема\",\n \"mode\": \"Режим\"\n },\n \"simpleMode\": \"Простой режим\",\n \"fullMode\": \"Полный режим\"\n },\n \"chat\": {\n \"modelSelectTooltip\": \"Выбор модели\",\n \"newChatTooltip\": \"Новый чат\",\n \"searchTooltip\": \"Поиск во всех чатах\",\n \"chatHistoryTooltip\": \"История чатов\",\n \"wideModeTooltip\": \"Широкий режим\",\n \"normalModeTooltip\": \"Обычный режим\",\n \"allChats\": \"Все чаты\",\n \"createNewChat\": \"Создать новый чат\",\n \"pinDrawer\": \"Закрепить\",\n \"unpinDrawer\": \"Открепить\",\n \"statusIdle\": \"Ожидание\",\n \"statusInProgress\": \"Выполняется\",\n \"disclaimer\": \"Работает для вас, растёт вместе с вами\",\n \"greeting\": \"Привет, чем я могу помочь вам сегодня?\",\n \"description\": \"Я полезный ассистент, который поможет вам с вашими вопросами.\",\n \"prompt1\": \"Давайте начнём новое путешествие!\",\n \"prompt2\": \"Расскажи мне, какими навыками ты обладаешь?\",\n \"inputPlaceholder\": \"\\\"↑↓\\\" для навигации по сообщениям · \\\"/\\\" для быстрых команд (\\\"/approve\\\" или \\\"/deny\\\" во время подтверждения)\",\n \"longTextUploadPrompt\": \"Пожалуйста, прочитайте загруженный файл с промптом и ответьте на его основе.\",\n \"emptyOutputError\": \"Ассистент вернул пустой ответ.\",\n \"commands\": {\n \"clear\": {\n \"description\": \"Очистить контекст диалога\"\n },\n \"compact\": {\n \"description\": \"Сжать контекст диалога и создать резюме; поддерживается доп. инструкция\"\n },\n \"approve\": {\n \"description\": \"Подтвердить ожидающее действие инструмента и продолжить выполнение\"\n },\n \"deny\": {\n \"description\": \"Отклонить ожидающее действие инструмента\"\n },\n \"mission\": {\n \"description\": \"Режим Mission — разбивает задачи на суб-агентов с изолированным контекстом\"\n },\n \"skills\": {\n \"description\": \"Показать доступные в чате навыки и явные команды их вызова\"\n },\n \"agents\": {\n \"description\": \"Показать агентов, доступных в текущей среде\"\n },\n \"review\": {\n \"description\": \"Проверить в Codex незакоммиченные изменения рабочей области\"\n },\n \"status\": {\n \"description\": \"Показать текущую учётную запись агента, модель и рабочую область\"\n },\n \"goal\": {\n \"description\": \"Goal-цикл — агент непрерывно преследует цель, автоматически оценивая завершение через Rubric\"\n }\n },\n \"attachments\": {\n \"tooltip\": \"Поддерживаются документы, изображения, видео, аудио и другие форматы\",\n \"tooltipImageOnly\": \"Текущая модель поддерживает только загрузку изображений\",\n \"tooltipNoMultimodal\": \"Мультимодальные возможности текущей модели не обнаружены, изображения или видео могут быть обработаны некорректно\",\n \"fileSizeLimit\": \"Максимум {{limit}} МБ на файл\",\n \"fileSizeExceeded\": \"Размер файла превышает лимит {{limit}} МБ. Текущий файл: {{size}} МБ\",\n \"multimodalDisabled\": \"Текущая модель не поддерживает загрузку файлов\",\n \"multimodalWarning\": \"Мультимодальные возможности текущей модели не обнаружены, изображения или видео могут быть обработаны некорректно\",\n \"imageOnlyAccept\": \"Текущая модель поддерживает только файлы изображений\",\n \"imageOnlyWarning\": \"Текущая модель поддерживает только изображения, видео и другие файлы могут быть обработаны некорректно\"\n },\n \"rateLimitHit\": \"Текущая модель ограничена. Попробуйте переключиться на другую бесплатную модель.\",\n \"turnUsagePopover\": {\n \"ariaLabel\": \"Показать использование токенов и контекста за ход\",\n \"turn\": \"Этот ход\",\n \"turnEstimated\": \"Этот ход ~\",\n \"tok\": \"tok\",\n \"inOut\": \"in {{inTok}} · out {{outTok}}\",\n \"contextLabel\": \"Окно контекста:\",\n \"manageContext\": \"Управление контекстом\",\n \"compact\": \"Сжать\",\n \"new\": \"Новый чат\"\n },\n \"rateLimitMessage\": \"Достигнут лимит запросов. Переключитесь на другую бесплатную модель:\",\n \"rateLimitSwitched\": \"Переключено на {{model}}\",\n \"speech\": {\n \"startRecording\": \"Начать запись голоса (Ctrl+Shift+M)\",\n \"stopRecording\": \"Остановить запись (Ctrl+Shift+M)\",\n \"transcribing\": \"Транскрибация...\",\n \"transcriptionDisabled\": \"Транскрибация отключена. Настройте провайдер в Настройки > Голосовая транскрибация.\",\n \"transcriptionFailed\": \"Ошибка голосовой транскрибации\",\n \"microphoneError\": \"Не удалось получить доступ к микрофону\",\n \"fileTooLarge\": \"Файл записи слишком большой ({{size}}МБ). Максимум: {{limit}}МБ\",\n \"recordingTooLong\": \"Длительность записи превышает лимит ({{limit}}с)\"\n },\n \"search\": {\n \"title\": \"Поиск в чате\",\n \"tooltip\": \"Поиск в текущем чате\",\n \"placeholder\": \"Поиск сообщений...\",\n \"noResults\": \"Результаты не найдены\",\n \"resultsCount\": \"Найдено {{count}} результат(ов)\",\n \"searching\": \"Поиск {{progress}}...\",\n \"loading\": \"Загрузка...\",\n \"userMessage\": \"Пользователь\",\n \"assistantMessage\": \"Ассистент\",\n \"titleMatch\": \"Название\"\n },\n \"contextMenu\": {\n \"open\": \"Открыть\",\n \"rename\": \"Переименовать\",\n \"pin\": \"Закрепить\",\n \"unpin\": \"Открепить\",\n \"delete\": \"Удалить\"\n },\n \"queue\": {\n \"title\": \"Очередь сообщений\",\n \"paused\": \"Приостановлено\",\n \"sendFailed\": \"Ошибка отправки\",\n \"resume\": \"Возобновить очередь\",\n \"pause\": \"Приостановить очередь\",\n \"clear\": \"Очистить очередь\",\n \"dragSort\": \"Перетащите для сортировки\",\n \"edit\": \"Редактировать\",\n \"retry\": \"Повторить\",\n \"skip\": \"Пропустить\",\n \"interruptAndSend\": \"Прервать и отправить\",\n \"delete\": \"Удалить\",\n \"file\": \"Файл\",\n \"noChatWarning\": \"Сначала отправьте сообщение, чтобы создать диалог, прежде чем использовать очередь\",\n \"queueFull\": \"Очередь полна (макс. {{max}} записей). Отправьте или удалите некоторые сообщения\",\n \"otherTabOwner\": \"This tab queues only; sending is handled by another tab\"\n },\n \"group\": {\n \"pinned\": \"Закреплённые\",\n \"today\": \"Сегодня\",\n \"week\": \"За 7 дней\",\n \"month\": \"За 30 дней\",\n \"older\": \"Ранее\"\n },\n \"sessionPanel\": {\n \"searchConversations\": \"Поиск…\",\n \"noResults\": \"Результатов нет\",\n \"noConversations\": \"Нет диалогов\",\n \"loadingConversations\": \"Загрузка…\"\n },\n \"conversationHistory\": \"История диалогов\",\n \"history\": {\n \"empty\": \"Нет истории чатов\"\n },\n \"sessionGroup\": {\n \"today\": \"Сегодня\",\n \"recent\": \"За 7 дней\",\n \"earlier\": \"Ранее\"\n }\n },\n \"security\": {\n \"parent\": \"Настройки\",\n \"security\": \"Безопасность\",\n \"title\": \"Безопасность\",\n \"description\": \"Управление функциями безопасности инструментов и навыков.\",\n \"toolGuardTitle\": \"Защита инструментов\",\n \"toolGuardDescription\": \"Настройте сканирование безопасности для вызовов инструментов. Опасные операции потребуют вашего явного подтверждения перед выполнением.\",\n \"enabled\": \"Включить защиту инструментов\",\n \"enabledTooltip\": \"При включении вызовы инструментов сканируются на наличие опасных шаблонов перед выполнением\",\n \"guardedTools\": \"Защищённые инструменты\",\n \"guardedToolsTooltip\": \"Инструменты, требующие подтверждения при обнаружении опасных шаблонов. Оставьте пустым для использования набора по умолчанию.\",\n \"guardedToolsPlaceholder\": \"Выберите инструменты или введите пользовательские имена\",\n \"deniedTools\": \"Запрещённые инструменты\",\n \"deniedToolsTooltip\": \"Инструменты, которые всегда отклоняются без запроса подтверждения\",\n \"deniedToolsPlaceholder\": \"Выберите инструменты для запрета\",\n \"saveSuccess\": \"Настройки защиты инструментов сохранены\",\n \"saveFailed\": \"Не удалось сохранить настройки защиты инструментов\",\n \"loadFailed\": \"Не удалось загрузить настройки защиты инструментов\",\n \"rules\": {\n \"title\": \"Правила обнаружения\",\n \"description\": \"Правила определяют шаблоны регулярных выражений, которые вызывают оповещения безопасности. Встроенные правила можно отключить; пользовательские правила можно добавлять, редактировать или удалять.\",\n \"id\": \"ID правила\",\n \"severity\": \"Серьёзность\",\n \"descriptionCol\": \"Описание\",\n \"source\": \"Источник\",\n \"actions\": \"Действия\",\n \"builtin\": \"Встроенное\",\n \"custom\": \"Пользовательское\",\n \"enable\": \"Включить\",\n \"disable\": \"Отключить\",\n \"edit\": \"Редактировать\",\n \"delete\": \"Удалить\",\n \"add\": \"Добавить правило\",\n \"addTitle\": \"Добавить пользовательское правило\",\n \"editTitle\": \"Редактировать пользовательское правило\",\n \"ruleId\": \"ID правила\",\n \"ruleIdRequired\": \"ID правила обязателен\",\n \"duplicateId\": \"Правило с таким ID уже существует\",\n \"tools\": \"Целевые инструменты\",\n \"toolsPlaceholder\": \"Оставьте пустым для совпадения со всеми инструментами\",\n \"params\": \"Целевые параметры\",\n \"paramsPlaceholder\": \"Оставьте пустым для совпадения со всеми параметрами\",\n \"severityLabel\": \"Серьёзность\",\n \"categoryLabel\": \"Категория\",\n \"patterns\": \"Шаблоны регулярных выражений\",\n \"patternsRequired\": \"Требуется хотя бы один шаблон\",\n \"patternsTooltip\": \"По одному регулярному выражению в строке. Сопоставляется со значениями параметров инструмента (без учёта регистра).\",\n \"excludePatterns\": \"Шаблоны исключения\",\n \"excludePatternsTooltip\": \"По одному регулярному выражению в строке. Если совпадает, правило пропускается.\",\n \"descriptionLabel\": \"Описание\",\n \"descriptionPlaceholder\": \"Что обнаруживает это правило?\",\n \"remediationLabel\": \"Рекомендация\",\n \"remediationPlaceholder\": \"Рекомендуемое действие при срабатывании правила\",\n \"preview\": \"Просмотр\",\n \"previewTitle\": \"Детали правила\",\n \"actionLabel\": \"Действие\",\n \"actionApproval\": \"Требуется одобрение\",\n \"autoDeny\": \"Автоотказ\",\n \"autoDenyTooltip\": \"При включении вызовы инструментов, соответствующие этому правилу, будут автоматически отклонены без ручного подтверждения\",\n \"autoDenyEnable\": \"Включить автоотказ для этого правила\",\n \"autoDenyDisable\": \"Отключить автоотказ для этого правила\",\n \"allTools\": \"Все инструменты\",\n \"allParams\": \"Все параметры\",\n \"descriptions\": {\n \"TOOL_CMD_CONTROL_CHARS\": \"Обнаруживает непечатаемые управляющие символы, которые могут обойти проверки безопасности\",\n \"TOOL_CMD_DANGEROUS_MV\": \"Обнаруживает команду mv, которая может неожиданно переместить или перезаписать файлы\",\n \"TOOL_CMD_DANGEROUS_RM\": \"Обнаруживает команду rm, которая может привести к потере данных\",\n \"TOOL_CMD_DOS_FORK_BOMB\": \"Обнаруживает классические Bash fork-бомбы и массовое завершение процессов\",\n \"TOOL_CMD_FIND_DELETE\": \"Обнаруживает команду 'find -delete', которая может привести к потере данных\",\n \"TOOL_CMD_FS_DESTRUCTION\": \"Обнаруживает команды низкоуровневого форматирования или очистки дисков\",\n \"TOOL_CMD_IFS_INJECTION\": \"Обнаруживает команды с переменной $IFS, которая может обойти проверку безопасности\",\n \"TOOL_CMD_JQ_FILE_FLAGS\": \"Обнаруживает команды jq с флагами для чтения произвольных файлов или выполнения кода\",\n \"TOOL_CMD_JQ_SYSTEM\": \"Обнаруживает команды jq с функцией system() для выполнения произвольных команд\",\n \"TOOL_CMD_OBFUSCATED_EXEC\": \"Обнаруживает выполнение строк в кодировке base64, передаваемых напрямую в интерпретатор оболочки\",\n \"TOOL_CMD_PIPE_TO_SHELL\": \"Обнаруживает паттерны 'curl | bash' для загрузки и немедленного выполнения удалённых скриптов\",\n \"TOOL_CMD_PRIVILEGE_ESCALATION\": \"Обнаруживает попытки повышения привилегий с использованием sudo, su, doas, pkexec или runas\",\n \"TOOL_CMD_PROCESS_KILL\": \"Обнаруживает команды завершения процессов, которые могут остановить критические процессы\",\n \"TOOL_CMD_PROC_ENVIRON\": \"Обнаруживает доступ к /proc/*/environ, который может раскрыть переменные окружения\",\n \"TOOL_CMD_REVERSE_SHELL\": \"Обнаруживает попытки создания обратных оболочек или несанкционированных сетевых туннелей\",\n \"TOOL_CMD_SERVICE_RESTART\": \"Обнаруживает команды управления службами, которые могут нарушить работу системных служб\",\n \"TOOL_CMD_SYSTEM_REBOOT\": \"Обнаруживает команды перезагрузки или выключения системы, которые остановят хост-систему\",\n \"TOOL_CMD_SYSTEM_TAMPERING\": \"Обнаруживает доступ к заданиям cron, SSH-ключам или правам sudo (включая чтение и изменение)\",\n \"TOOL_CMD_UNICODE_WHITESPACE\": \"Обнаруживает пробельные символы Unicode, вызывающие неоднозначность парсинга\",\n \"TOOL_CMD_UNSAFE_PERMISSIONS\": \"Обнаруживает глобальное понижение прав доступа (chmod 777) или установку неизменяемых флагов\",\n \"TOOL_CMD_ZSH_DANGEROUS\": \"Обнаруживает Zsh-специфичные встроенные команды, которые могут обойти проверки безопасности\"\n },\n \"categories\": {\n \"command_injection\": \"Внедрение команд\",\n \"code_execution\": \"Выполнение кода\",\n \"data_exfiltration\": \"Утечка данных\",\n \"path_traversal\": \"Обход пути\",\n \"sensitive_file_access\": \"Доступ к конфиденциальным файлам\",\n \"network_abuse\": \"Злоупотребление сетью\",\n \"credential_exposure\": \"Утечка учётных данных\",\n \"resource_abuse\": \"Злоупотребление ресурсами\",\n \"privilege_escalation\": \"Повышение привилегий\",\n \"prompt_injection\": \"Внедрение промпта\",\n \"other\": \"Другое\"\n }\n },\n \"fileGuard\": {\n \"title\": \"Защита файлов\",\n \"description\": \"Защита конфиденциальных файлов и директорий от доступа инструментов агента. Добавленные пути будут заблокированы для всех вызовов инструментов.\",\n \"enableLabel\": \"Включить защиту файлов\",\n \"path\": \"Путь\",\n \"actions\": \"Действия\",\n \"add\": \"Добавить\",\n \"inputPlaceholder\": \"Введите путь к файлу или директории (например, ~/.ssh/ или /etc/passwd)\",\n \"empty\": \"Пользовательские конфиденциальные пути не настроены\",\n \"directory\": \"Директория\",\n \"removeConfirm\": \"Удалить этот путь из списка защиты?\",\n \"duplicate\": \"Этот путь уже есть в списке\",\n \"saveSuccess\": \"Настройки защиты файлов сохранены\",\n \"saveFailed\": \"Не удалось сохранить настройки защиты файлов\",\n \"loadFailed\": \"Не удалось загрузить настройки защиты файлов\",\n \"allowPreviewOutsideWorkspace\": \"Разрешить предпросмотр файлов вне рабочего пространства\",\n \"allowPreviewOutsideWorkspaceDesc\": \"Разрешить предпросмотр файлов за пределами рабочей директории в консоли. Защита конфиденциальных файлов остаётся активной.\"\n },\n \"allowNoAuthHosts\": {\n \"title\": \"Хосты без аутентификации\",\n \"warningTitle\": \"Предупреждение безопасности\",\n \"warningDescription\": \"IP-адреса в этом списке могут обращаться к конечным точкам API без аутентификации. По умолчанию разрешены локальные хосты (127.0.0.1 и ::1) для доступа через CLI. Добавляйте только доверенные IP-адреса. ВНИМАНИЕ: Добавление недоверенных IP представляет серьезную угрозу безопасности.\",\n \"ipAddress\": \"IP-адрес\",\n \"actions\": \"Действия\",\n \"add\": \"Добавить\",\n \"inputPlaceholder\": \"Введите IP-адрес (например, 192.168.1.100 или ::1)\",\n \"empty\": \"IP-адреса не настроены\",\n \"default\": \"По умолчанию\",\n \"removeConfirm\": \"Удалить этот IP-адрес из белого списка?\",\n \"duplicate\": \"Этот IP-адрес уже есть в списке\",\n \"invalidIP\": \"Неверный формат IP-адреса\",\n \"saveSuccess\": \"Настройки хостов без аутентификации сохранены\",\n \"saveFailed\": \"Не удалось сохранить настройки хостов без аутентификации\",\n \"loadFailed\": \"Не удалось загрузить настройки хостов без аутентификации\"\n },\n \"skillScanner\": {\n \"title\": \"Сканер навыков\",\n \"description\": \"Автоматическое сканирование навыков на наличие угроз безопасности перед активацией или установкой.\",\n \"mode\": \"Режим сканирования\",\n \"modeTooltip\": \"Управление поведением сканера: блокировка, только предупреждение или отключение\",\n \"modeBlock\": \"Блокировать\",\n \"modeWarn\": \"Только предупреждение\",\n \"modeOff\": \"Выключен\",\n \"timeout\": \"Таймаут сканирования (секунды)\",\n \"timeoutTooltip\": \"Максимальное время ожидания завершения сканирования (5-300 секунд)\",\n \"saveSuccess\": \"Настройки сканера навыков сохранены\",\n \"saveFailed\": \"Не удалось сохранить настройки сканера навыков\",\n \"scanAlerts\": {\n \"title\": \"Оповещения сканирования\",\n \"empty\": \"Нет оповещений безопасности\",\n \"clearAll\": \"Очистить все\",\n \"clearConfirm\": \"Очистить все оповещения сканирования?\",\n \"skillName\": \"Навык\",\n \"action\": \"Действие\",\n \"actionBlocked\": \"Заблокировано\",\n \"actionWarned\": \"Предупреждение\",\n \"time\": \"Время\",\n \"actions\": \"Действия\",\n \"allowSkill\": \"Добавить в белый список\",\n \"remove\": \"Удалить\",\n \"viewFindings\": \"Подробности\"\n },\n \"whitelist\": {\n \"title\": \"Белый список\",\n \"empty\": \"Белый список пуст\",\n \"skillName\": \"Навык\",\n \"addedAt\": \"Добавлено\",\n \"contentHash\": \"Хеш содержимого\",\n \"actions\": \"Действия\",\n \"remove\": \"Удалить\",\n \"removeConfirm\": \"Удалить этот навык из белого списка?\",\n \"addSuccess\": \"Навык добавлен в белый список\",\n \"removeSuccess\": \"Навык удалён из белого списка\",\n \"addFailed\": \"Не удалось добавить навык в белый список\",\n \"removeFailed\": \"Не удалось удалить навык из белого списка\",\n \"removeWillDisable\": \"После удаления навык также будет отключён.\",\n \"removeAndDisabled\": \"Навык удалён из белого списка и отключён\"\n },\n \"scanError\": {\n \"title\": \"Обнаружены проблемы безопасности\",\n \"description\": \"Обнаружены следующие проблемы безопасности:\",\n \"warnDescription\": \"Обнаружены проблемы безопасности, но навык был включён (режим предупреждения):\",\n \"goToWhitelist\": \"Перейти к белому списку\",\n \"moreFindings\": \"Ещё {{count}} записей не показано (полный список: Настройки → Безопасность → Сканер навыков).\"\n }\n }\n },\n \"login\": {\n \"title\": \"Вход в QwenPaw\",\n \"registerTitle\": \"Создание аккаунта\",\n \"firstUserHint\": \"Создайте учётную запись администратора для начала работы\",\n \"usernamePlaceholder\": \"Имя пользователя\",\n \"passwordPlaceholder\": \"Пароль\",\n \"usernameRequired\": \"Пожалуйста, введите имя пользователя\",\n \"passwordRequired\": \"Пожалуйста, введите пароль\",\n \"submit\": \"Войти\",\n \"register\": \"Зарегистрироваться\",\n \"failed\": \"Ошибка входа, проверьте учётные данные\",\n \"registerSuccess\": \"Регистрация прошла успешно\",\n \"registerFailed\": \"Ошибка регистрации\",\n \"authNotEnabled\": \"Аутентификация не включена\",\n \"logout\": \"Выйти\",\n \"logoutSuccess\": \"Вы успешно вышли\",\n \"sessionExpired\": \"Сессия истекла, пожалуйста, войдите снова\",\n \"unauthorized\": \"Не авторизован, пожалуйста, войдите\"\n },\n \"account\": {\n \"title\": \"Управление аккаунтом\",\n \"currentPassword\": \"Текущий пароль\",\n \"currentPasswordRequired\": \"Пожалуйста, введите текущий пароль\",\n \"newUsername\": \"Новое имя пользователя\",\n \"newUsernamePlaceholder\": \"Оставьте пустым, чтобы не менять\",\n \"newPassword\": \"Новый пароль\",\n \"newPasswordPlaceholder\": \"Оставьте пустым, чтобы не менять\",\n \"confirmPassword\": \"Подтвердите новый пароль\",\n \"confirmPasswordPlaceholder\": \"Введите новый пароль ещё раз\",\n \"passwordMismatch\": \"Пароли не совпадают\",\n \"save\": \"Сохранить изменения\",\n \"updateSuccess\": \"Аккаунт успешно обновлён\",\n \"updateFailed\": \"Не удалось обновить аккаунт\",\n \"wrongPassword\": \"Текущий пароль неверен\",\n \"nothingToUpdate\": \"Пожалуйста, введите новое имя пользователя или пароль\",\n \"passwordEmpty\": \"Пароль не может быть пустым\",\n \"usernameEmpty\": \"Имя пользователя не может быть пустым\"\n },\n \"voiceTranscription\": {\n \"title\": \"Транскрипция голоса\",\n \"description\": \"Настройте обработку входящих аудио- и голосовых сообщений.\",\n \"loadFailed\": \"Не удалось загрузить настройки аудиорежима\",\n \"saveSuccess\": \"Аудиорежим сохранён\",\n \"saveFailed\": \"Не удалось сохранить аудиорежим\",\n \"audioModeLabel\": \"Аудиорежим\",\n \"audioModeDescription\": \"Выберите, как голосовые сообщения из каналов (Discord, Telegram и др.) обрабатываются перед отправкой модели.\",\n \"modeAuto\": \"Авто (рекомендуется)\",\n \"modeAutoDesc\": \"Транскрибировать аудио в текст через выбранный провайдер, затем отправить текст модели. Если транскрипция отключена или недоступна, отображается заглушка «файл загружен». В этом режиме аудио не отправляется модели напрямую. Работает со всеми моделями.\",\n \"modeNative\": \"Нативное аудио\",\n \"modeNativeDesc\": \"Отправлять аудиофайл модели напрямую без транскрипции. Это единственный режим, передающий аудио модели. Работает только с моделями, поддерживающими аудио (например, gpt-4o-audio). Большинство моделей не поддерживают этот режим.\",\n \"ffmpegReady\": \"ffmpeg установлен. Конвертация аудио для нативного режима доступна.\",\n \"ffmpegMissing\": \"ffmpeg не установлен.\",\n \"ffmpegMissingDesc\": \"Нативный аудиорежим требует ffmpeg для конвертации аудиоформатов (например, .ogg в .wav). Установите ffmpeg как системный пакет для включения этого режима.\",\n \"providerTypeLabel\": \"Провайдер транскрипции\",\n \"providerTypeDescription\": \"Выберите бэкенд транскрипции. Если голосовая транскрипция не нужна, выберите «Отключено».\",\n \"providerTypeDisabled\": \"Отключено\",\n \"providerTypeDisabledDesc\": \"Без транскрипции. Голосовые сообщения будут отображаться как заглушка «файл загружен».\",\n \"providerTypeWhisperApi\": \"Whisper API\",\n \"providerTypeWhisperApiDesc\": \"Использовать OpenAI-совместимый Whisper API от настроенного провайдера (например, OpenAI, Ollama).\",\n \"providerTypeLocalWhisper\": \"Локальный Whisper\",\n \"providerTypeLocalWhisperDesc\": \"Запускать транскрипцию локально с помощью библиотеки openai-whisper. Требуются ffmpeg и openai-whisper.\",\n \"localWhisperReady\": \"Локальный Whisper готов к работе. ffmpeg и openai-whisper установлены.\",\n \"localWhisperMissing\": \"Локальный Whisper не готов. Необходимо установить недостающие зависимости.\",\n \"localWhisperMissingDesc\": \"ffmpeg: {{ffmpeg}} | openai-whisper: {{whisper}}. Установите недостающие зависимости: ffmpeg (системный пакет) и openai-whisper (uv pip install openai-whisper или установите QwenPaw с опцией [whisper]).\",\n \"providerLabel\": \"Провайдер Whisper API\",\n \"providerDescription\": \"Выберите провайдера для транскрипции аудио через Whisper API. Отображаются только провайдеры с совместимым эндпоинтом.\",\n \"providerPlaceholder\": \"Выберите провайдера...\",\n \"noProvidersWarning\": \"Не найдено провайдеров для транскрипции. Настройте провайдер OpenAI для включения голосовой транскрипции.\",\n \"transcriptionInfoTitle\": \"Как работает транскрипция\",\n \"transcriptionInfoDesc\": \"Whisper API транскрипция использует OpenAI-совместимый эндпоинт /v1/audio/transcriptions. Необходим провайдер с поддержкой Whisper (например, OpenAI). Выберите конкретного провайдера выше для включения транскрипции.\",\n \"transcriptionInfoDescLocal\": \"Локальная Whisper транскрипция запускает библиотеку openai-whisper непосредственно на вашем устройстве. Требуются ffmpeg (для декодирования аудио) и пакет openai-whisper. API-ключ и подключение к интернету не нужны. Установка: uv pip install 'qwenpaw[whisper]'.\"\n },\n \"acp\": {\n \"title\": \"ACP\",\n \"loading\": \"Загрузка конфигурации ACP...\",\n \"builtin\": \"Встроенный\",\n \"custom\": \"Пользовательский\",\n \"create\": \"Добавить пользовательский агент\",\n \"createTitle\": \"Создать ACP агент\",\n \"createSuccess\": \"ACP агент создан\",\n \"editTitle\": \"Редактировать конфигурацию ACP\",\n \"agentKey\": \"Ключ агента\",\n \"agentKeyRequired\": \"Пожалуйста, введите ключ агента\",\n \"agentKeyInvalid\": \"Ключ агента может содержать только буквы, цифры, подчёркивания и дефисы\",\n \"agentKeyExists\": \"Этот ключ агента уже существует\",\n \"enabled\": \"Включено\",\n \"command\": \"Команда\",\n \"commandRequired\": \"Пожалуйста, введите команду\",\n \"args\": \"Аргументы\",\n \"argsHelp\": \"По одному аргументу на строку\",\n \"env\": \"Переменные окружения\",\n \"envHelp\": \"Используйте формат KEY=VALUE в каждой строке\",\n \"envInvalidLine\": \"Неверный формат переменной окружения: {{line}}\",\n \"trusted\": \"Доверенный\",\n \"toolParseMode\": \"Режим разбора инструментов\",\n \"toolParseModeRequired\": \"Пожалуйста, выберите режим разбора инструментов\",\n \"stdioBufferLimit\": \"Лимит буфера stdio\",\n \"stdioBufferLimitHelp\": \"Максимальный размер буфера чтения одной строки stdio для ACP-подпроцесса в байтах.\",\n \"stdioBufferLimitRequired\": \"Пожалуйста, укажите лимит буфера stdio\",\n \"stdioBufferLimitMin\": \"Лимит буфера stdio должен быть не меньше 1 байта\",\n \"docs\": \"Документация\",\n \"docsHelp\": \"Открыть документацию по ACP и перейти к разделу \\\"How to configure external runners\\\"\",\n \"notSet\": \"Не задано\",\n \"configSaved\": \"Конфигурация ACP сохранена\",\n \"configFailed\": \"Не удалось сохранить конфигурацию ACP\",\n \"deleteTitle\": \"Удалить {{name}}\",\n \"deleteConfirm\": \"Удалить этот ACP агент? Это действие нельзя отменить.\",\n \"deleteSuccess\": \"ACP агент удалён\",\n \"deleteFailed\": \"Не удалось удалить ACP агент\",\n \"nodeSettings\": \"Настройки Node\",\n \"nodePath\": \"Путь к Node\",\n \"chooseOtherNode\": \"Выбрать другой Node...\",\n \"selectNodePath\": \"Выбрать путь к Node\",\n \"nodePathPrompt\": \"Введите путь к исполняемому файлу node\",\n \"nodeSaved\": \"Настройки Node сохранены\",\n \"nodeLoadFailed\": \"Не удалось загрузить настройки Node\",\n \"nodeSaveFailed\": \"Путь к Node недоступен\",\n \"nodeRuntime\": {\n \"bundled\": \"Встроенный Node\",\n \"system\": \"Системный Node\",\n \"custom\": \"Пользовательский Node\"\n },\n \"nodeRuntimeReason\": {\n \"systemNodeMissing\": \"Системный Node не обнаружен\",\n \"nodeMissing\": \"Путь к Node не существует\",\n \"npxMissing\": \"npx не найден\",\n \"versionCheckFailed\": \"Проверка версии не удалась\",\n \"unavailable\": \"Недоступно\"\n }\n },\n \"backup\": {\n \"title\": \"Резервные копии\",\n \"description\": \"Резервное копирование и восстановление рабочих пространств агентов и конфигураций.\",\n \"create\": \"Создать резервную копию\",\n \"import\": \"Импорт\",\n \"export\": \"Экспорт\",\n \"restore\": \"Восстановить\",\n \"delete\": \"Удалить\",\n \"batchDelete\": \"Удалить выбранные\",\n \"batchDeleteConfirm\": \"Вы уверены, что хотите удалить {{count}} резервную(ых) копию(й)? Это действие нельзя отменить.\",\n \"deleteConfirm\": \"Вы уверены, что хотите удалить эту резервную копию? Это действие нельзя отменить.\",\n \"name\": \"Название\",\n \"createdAt\": \"Создана\",\n \"scopeSummary\": \"Область\",\n \"descriptionLabel\": \"Описание\",\n \"noBackups\": \"Резервных копий пока нет. Создайте одну для сохранения текущей конфигурации.\",\n \"loadFailed\": \"Не удалось загрузить список резервных копий\",\n \"createTitle\": \"Создать резервную копию\",\n \"backupMode\": \"Режим резервного копирования\",\n \"fullBackup\": \"Полная резервная копия\",\n \"fullBackupDesc\": \"Резервное копирование всего, включая все рабочие пространства агентов, глобальные настройки, пул навыков и секреты\",\n \"customBackup\": \"Пользовательская резервная копия\",\n \"partialBackup\": \"Частичная резервная копия\",\n \"partialBackupDesc\": \"Настроить содержимое резервной копии\",\n \"nameRequired\": \"Название резервной копии обязательно\",\n \"namePlaceholder\": \"Введите название резервной копии\",\n \"descriptionPlaceholder\": \"Необязательное описание...\",\n \"scopeAgents\": \"Рабочие пространства агентов\",\n \"scopeGlobalConfig\": \"Глобальные настройки\",\n \"scopeSkillPool\": \"Пул навыков\",\n \"scopeSecrets\": \"Секреты\",\n \"scopeSecretsHint\": \"Включает секреты провайдеров моделей (API-ключи), переменные окружения и другую конфиденциальную информацию\",\n \"securityNotice\": \"Эта резервная копия может содержать конфиденциальные учётные данные. Рабочие пространства агентов включают учётные данные каналов (токены ботов, секреты приложений и т.д.), а секреты провайдеров моделей включают API-ключи. Не передавайте файлы резервных копий другим лицам.\",\n \"searchPlaceholder\": \"Поиск по названию или ID...\",\n \"total\": \"Всего {{count}}\",\n \"agentsPlaceholder\": \"Выберите рабочие пространства агентов (оставьте пустым для всех)\",\n \"selectAll\": \"Выбрать все\",\n \"deselectAll\": \"Снять выбор\",\n \"createSuccess\": \"Резервная копия успешно создана\",\n \"createFailed\": \"Не удалось создать резервную копию\",\n \"restoreTitle\": \"Восстановить резервную копию\",\n \"restoreScope\": \"Область восстановления\",\n \"restoreFullWarning\": \"Полное восстановление полностью заменит всё содержимое текущего экземпляра (всех агентов, глобальную конфигурацию, пул навыков и секреты). Это действие нельзя отменить. Во время восстановления связанные функции будут недоступны. После завершения восстановления перезапустите сервис.\",\n \"agentGroupExisting\": \"Существующие\",\n \"agentGroupNew\": \"Новые\",\n \"agentExists\": \"Существует\",\n \"agentNew\": \"Новый\",\n \"agentActionReplace\": \"Заменить\",\n \"agentActionAdd\": \"Добавить\",\n \"agentColumnName\": \"Агент\",\n \"agentColumnWorkspace\": \"Рабочая область\",\n \"agentTotal\": \"Всего {{count}} агентов\",\n \"noAgentsInBackup\": \"В резервной копии нет рабочих областей агентов\",\n \"loadingAgents\": \"Загрузка списка агентов...\",\n \"agentSearchPlaceholder\": \"Поиск по имени или ID...\",\n \"agentSearchTotal\": \"{{count}} из {{total}} агентов\",\n \"restoreCustomSummary\": \"Выбрано: {{existing}} существующих · {{added}} новых\",\n \"defaultWorkspaceDirUnused\": \"Нет новых агентов\",\n \"otherOptions\": \"Другие параметры\",\n \"defaultWorkspaceDirDefault\": \"<Путь по умолчанию>/{{aid}}\",\n \"restoreWarningModify\": \"Восстановление изменит текущие конфигурации. Это действие нельзя отменить.\",\n \"restoreWarningRestart\": \"Во время восстановления связанные функции будут недоступны. После завершения восстановления перезапустите сервис.\",\n \"preRestoreBackupTitle\": \"Создать резервную копию перед восстановлением\",\n \"preRestoreBackupContent\": \"Операция восстановления необратима. Хотите сначала создать резервную копию текущего состояния? Это позволит откатиться, если во время восстановления возникнут проблемы.\",\n \"preRestoreBackupYes\": \"Да, сначала создать резервную копию\",\n \"preRestoreBackupNo\": \"Нет, восстановить сразу\",\n \"preRestoreBackupDesc\": \"Автоматически созданная резервная копия перед восстановлением\",\n \"preRestoreBackupFailed\": \"Не удалось создать резервную копию перед восстановлением\",\n \"preRestoreBackupSuccess\": \"Резервная копия создана успешно, ID: {{id}}\",\n \"creatingPreRestoreBackup\": \"Создание резервной копии перед восстановлением...\",\n \"skipAndRestore\": \"Пропустить и продолжить восстановление\",\n \"restoreConfirm\": \"Я подтверждаю, что хочу восстановить эту резервную копию\",\n \"restoreMode\": \"Режим восстановления\",\n \"restoreModeFull\": \"Полное восстановление\",\n \"restoreModeFullDesc\": \"Полная замена всего содержимого текущего экземпляра, включая всех агентов, глобальную конфигурацию, пул навыков и секреты\",\n \"restoreModeCustom\": \"Выборочное восстановление\",\n \"restoreModeCustomDesc\": \"Выберите содержимое для восстановления, например, отдельных агентов, конфигурацию и т.д.; существующие агенты, не включённые в это восстановление, сохранятся\",\n \"restoreModeFullDisabled\": \"Только для полной резервной копии\",\n \"defaultWorkspaceDir\": \"Каталог рабочей области агента по умолчанию\",\n \"defaultWorkspaceDirPlaceholder\": \"Оставьте пустым для использования ~/.qwenpaw/workspaces\",\n \"defaultWorkspaceDirHint\": \"Если исходный путь рабочей области агента не существует, агент будет сохранён по пути <путь по умолчанию>/<ID агента>. Если путь существует, используется исходный путь агента.\",\n \"restoreStrategy\": \"Стратегия восстановления\",\n \"restoreStrategyPreserve\": \"Сохранить локальные настройки безопасности и MCP\",\n \"restoreStrategyPreserveDesc\": \"Сохранить защитные настройки безопасности и конфигурацию MCP этого экземпляра.\",\n \"restoreStrategyRestore\": \"Восстановить эти настройки из резервной копии\",\n \"restoreStrategyRestoreDesc\": \"Использовать настройки безопасности и MCP из резервной копии.\",\n \"restoreSuccess\": \"Резервная копия успешно восстановлена. Пожалуйста, перезапустите сервис.\",\n \"restoreSuccessPreserved\": \"Резервная копия успешно восстановлена. Сохранены локальные настройки: {{keys}}. Пожалуйста, перезапустите сервис.\",\n \"restoreSuccessWithBackup\": \"Резервная копия успешно восстановлена. ID резервной копии до восстановления: {{preRestoreId}}. Пожалуйста, перезапустите сервис.\",\n \"restoreFailed\": \"Не удалось восстановить резервную копию\",\n \"restoreTargetBusy\": \"Не удалось восстановить резервную копию: эти каталоги все еще используются. Закройте браузер или процесс, который их использует, и повторите попытку. Если каталоги остаются заблокированными, перезапустите систему и попробуйте снова.\",\n \"detailLoadFailed\": \"Не удалось загрузить детали резервной копии; восстановление недоступно\",\n \"trustLocalBanner\": \"Локальная резервная копия — по умолчанию полное восстановление\",\n \"trustForeignBanner\": \"Импортированная резервная копия — локальные настройки безопасности и MCP сохраняются по умолчанию\",\n \"trustLegacyBanner\": \"Устаревшая резервная копия — перед восстановлением нужно подтвердить доверие\",\n \"trustForeignTitle\": \"Доверять этой резервной копии?\",\n \"trustLegacyTitle\": \"Доверять устаревшей резервной копии?\",\n \"unknownBackupName\": \"Архив резервной копии\",\n \"trustForeignDesc\": \"Эта резервная копия не подписана данным экземпляром. Продолжайте только если доверяете источнику; при восстановлении локальные настройки безопасности и MCP будут сохранены по умолчанию.\",\n \"trustLegacyDesc\": \"У этой старой резервной копии нет локальной подписи. Продолжайте только если доверяете источнику; этот экземпляр подпишет ее перед восстановлением.\",\n \"deleteSuccess\": \"Резервная(ые) копия(и) успешно удалена(ы)\",\n \"deleteFailed\": \"Не удалось удалить резервную(ые) копию(и)\",\n \"exportFailed\": \"Не удалось экспортировать резервную копию\",\n \"exportWarningTitle\": \"Предупреждение о конфиденциальной информации\",\n \"exportWarningContent\": \"Эта резервная копия может содержать конфиденциальную информацию учетных данных. Рабочие пространства агентов содержат учетные данные каналов (такие как токены ботов, секреты приложений и т.д.), а секреты поставщиков моделей содержат API-ключи. Не передавайте файл резервной копии другим лицам.\",\n \"exportConfirm\": \"Подтвердить экспорт\",\n \"importTitle\": \"Импорт резервной копии\",\n \"importSuccess\": \"Резервная копия успешно импортирована\",\n \"importFailed\": \"Не удалось импортировать резервную копию\",\n \"importConflictTitle\": \"Резервная копия уже существует\",\n \"importConflictDesc\": \"Резервная копия с таким же ID уже существует. Хотите заменить её?\",\n \"importReplace\": \"Заменить\",\n \"agents\": \"{{count}} агент(ов)\",\n \"allAgents\": \"Все агенты\",\n \"globalConfig\": \"Глобальные настройки\",\n \"skillPool\": \"Пул навыков\",\n \"secrets\": \"Секреты\",\n \"selected\": \"Выбрано {{count}}\",\n \"fileCount\": \"{{count}} файлов\",\n \"totalSize\": \"{{size}}\",\n \"progressStarting\": \"Инициализация резервной копии...\",\n \"progressAgent\": \"Резервное копирование агента {{index}} / {{total}}...\",\n \"progressSaving\": \"Сохранение файла резервной копии...\",\n \"progressDone\": \"Резервная копия создана\",\n \"localModelsNotice\": \"Файлы резервной копии не включают локальные файлы моделей. При миграции между устройствами необходимо повторно загрузить нужные локальные модели на целевом устройстве.\"\n },\n \"approval\": {\n \"title\": \"Требуется подтверждение безопасности\",\n \"tool\": \"Инструмент\",\n \"severity\": \"Уровень угрозы\",\n \"findings\": \"Находки\",\n \"parameters\": \"Параметры\",\n \"approve\": \"Подтвердить\",\n \"deny\": \"Отклонить\",\n \"source\": \"Источник\",\n \"subSession\": \"Подагент\",\n \"approved\": \"Вызов инструмента подтвержден\",\n \"denied\": \"Вызов инструмента отклонен\",\n \"approveFailed\": \"Не удалось подтвердить\",\n \"denyFailed\": \"Не удалось отклонить\",\n \"approvePattern\": \"Всегда Разрешить\",\n \"strictModeHint\": \"Всегда разрешить недоступно в строгом режиме\",\n \"approveExact\": \"Только Сейчас\",\n \"approvalScope\": \"Область подтверждения\",\n \"details\": \"Подробности\",\n \"timeoutAutoDenied\": \"Время истекло, автоматически отклонено\",\n \"acknowledge\": \"Понятно\",\n \"builtinSource\": \"Встроенный\",\n \"ownerAgent\": \"Владелец агента\",\n \"executingAgent\": \"Исполняющий агент\"\n },\n \"pluginManager\": {\n \"title\": \"Менеджер плагинов\",\n \"description\": \"Установка, управление и удаление плагинов без перезапуска службы.\",\n \"installed\": \"Установленные плагины\",\n \"noPlugins\": \"Плагины еще не установлены\",\n \"installBtn\": \"Установить плагин\",\n \"installTitle\": \"Установка плагина\",\n \"dropPrimary\": \"Перетащите папку или ZIP-файл сюда\",\n \"dropSecondary\": \"или нажмите, чтобы выбрать ZIP-файл\",\n \"orFromUrl\": \"или из URL\",\n \"installFromUrl\": \"Установить из URL\",\n \"urlPlaceholder\": \"https://example.com/plugin.zip\",\n \"zipOnly\": \"При перетаскивании файлов поддерживаются только ZIP-файлы — перетащите папку для установки из директории\",\n \"dropFailed\": \"Не удалось прочитать перетащенную папку\",\n \"restartHint\": \"Некоторые плагины (например, хуки, monkey-patches) могут требовать перезапуска приложения для полного вступления в силу или удаления.\",\n \"installing\": \"Установка...\",\n \"installSuccess\": \"Плагин успешно установлен\",\n \"installFailed\": \"Не удалось установить плагин\",\n \"uninstall\": \"Удалить\",\n \"uninstallConfirm\": \"Удалить плагин \\\"{{name}}\\\"? Это действие нельзя отменить. Некоторые плагины могут требовать перезапуска приложения для полного удаления.\",\n \"uninstalling\": \"Удаление...\",\n \"uninstallSuccess\": \"Плагин успешно удалён\",\n \"uninstallFailed\": \"Не удалось удалить плагин\",\n \"statusLoaded\": \"Работает\",\n \"statusUnloaded\": \"Не загружен\",\n \"type\": \"Тип\",\n \"version\": \"Версия\",\n \"author\": \"Автор\",\n \"unknown\": \"Неизвестно\",\n \"loadFailed\": \"Не удалось загрузить список плагинов\",\n \"confirmTitle\": \"Подтвердить удаление\",\n \"officialTitle\": \"Официальные плагины\",\n \"officialDesc\": \"Установка опубликованных плагинов с CDN загрузок.\",\n \"catalogRefresh\": \"Обновить\",\n \"catalogEmpty\": \"Нет доступных официальных плагинов (каталог CDN может быть пуст)\",\n \"catalogLoadFailed\": \"Не удалось загрузить каталог официальных плагинов\",\n \"catalogInstall\": \"Установить\",\n \"catalogReinstall\": \"Переустановить\",\n \"catalogUpgrade\": \"Доступно обновление\",\n \"catalogUpgradeBtn\": \"Обновить\",\n \"catalogInstalled\": \"Установлен\",\n \"catalogSha256\": \"SHA256\",\n \"kindBundle\": \"Пакет\",\n \"kindTool\": \"Инструмент\",\n \"filterByName\": \"Фильтр по имени\",\n \"filterByKind\": \"Фильтр по типу\",\n \"marketTitle\": \"Plugin Market\",\n \"marketEmpty\": \"No plugins found in market\",\n \"marketLoadFailed\": \"Failed to load plugin market\",\n \"marketUnavailable\": \"Plugin Market is currently under maintenance, please try again later\",\n \"marketAll\": \"All\",\n \"marketSearch\": \"Search plugins...\",\n \"marketCategory\": \"Filter by category\",\n \"marketSearchResult\": \"Found {{count}} plugins matching \\\"{{keyword}}\\\"\",\n \"marketDetails\": \"Details\",\n \"marketDownloads\": \"Downloads\",\n \"marketDeveloper\": \"Developer\",\n \"marketSortLabel\": \"Sort plugins\",\n \"marketSortDownloads\": \"Most downloaded\",\n \"marketSortUpdated\": \"Recently updated\",\n \"marketSortFavorites\": \"Most favorited\"\n },\n \"inbox\": {\n \"title\": \"Ежедневный AI-сбор\",\n \"tabApprovals\": \"Подтверждения\",\n \"tabPushMessages\": \"Push-сообщения\",\n \"tabHarvests\": \"AI-сбор\",\n \"summaryApprovals\": \"Подтверждения\",\n \"summaryPushUnread\": \"Непрочитанные push\",\n \"summaryHarvests\": \"Сборы\",\n \"from\": \"От:\",\n \"requestedBy\": \"Кем:\",\n \"markRead\": \"Прочитано\",\n \"markAllRead\": \"Прочитать всё\",\n \"markAllReadNoUnread\": \"Нет непрочитанных сообщений\",\n \"markAllReadSuccess\": \"Отмечено как прочитанное: {{count}} сообщ.\",\n \"selectAllCurrentPage\": \"Выбрать текущую страницу\",\n \"selectedItems\": \"Выбрано: {{count}}\",\n \"batchDeleteButton\": \"Удалить выбранные\",\n \"batchOperation\": \"Массовые действия\",\n \"exitBatch\": \"Выйти\",\n \"batchDeleteConfirm\": \"Удалить {{count}} выбранных push-сообщений?\",\n \"batchDeleteSuccess\": \"Удалено push-сообщений: {{count}}\",\n \"pushCronHeader\": \"Cron-задача: {{name}}\",\n \"viewDetails\": \"Подробнее\",\n \"deleteMessageConfirm\": \"Удалить это push-сообщение?\",\n \"viewAll\": \"Показать все\",\n \"read\": \"Читать\",\n \"approve\": \"Подтвердить\",\n \"reject\": \"Отклонить\",\n \"ready\": \"Готово\",\n \"harvestNow\": \"Собрать сейчас\",\n \"createHarvest\": \"Создать сбор\",\n \"createFirstHarvest\": \"Создайте первый сбор\",\n \"createSuccess\": \"Сбор успешно создан\",\n \"emptyApprovals\": \"Нет ожидающих подтверждений\",\n \"emptyPush\": \"Нет push-сообщений\",\n \"wobbleEnable\": \"Включить уведомление о подтверждении\",\n \"wobbleDisable\": \"Отключить уведомление о подтверждении\",\n \"emptyHarvests\": \"Сборов пока нет\",\n \"statusReadyToHarvest\": \"Готово к сбору\",\n \"statusGrowing\": \"Растёт\",\n \"harvestedTimes\": \"Собрано раз: {{count}}\",\n \"harvestSuccessRate\": \"Успешность {{rate}}%\",\n \"messageDetailComingSoon\": \"Подробный вид сообщения — скоро!\",\n \"harvestSettingsComingSoon\": \"Настройки сбора — скоро!\",\n \"messageDetailTitle\": \"Детали выполнения\",\n \"detailCronTitle\": \"Cron-задача: {{name}}\",\n \"detailHeartbeatTitle\": \"Пульс\",\n \"messageNotFound\": \"Сообщение не найдено\",\n \"detailTaskName\": \"Имя задачи\",\n \"detailTaskId\": \"ID задачи\",\n \"detailStatus\": \"Статус\",\n \"detailSeverity\": \"Важность\",\n \"detailAgent\": \"Агент\",\n \"detailSource\": \"Источник\",\n \"detailSourceCronJob\": \"Cron-задача\",\n \"detailSourceHeartbeat\": \"Пульс\",\n \"detailTrigger\": \"Триггер\",\n \"detailDuration\": \"Длительность\",\n \"detailExecutedAt\": \"Выполнено в\",\n \"detailReceivedAt\": \"Получено в\",\n \"detailRunId\": \"ID запуска\",\n \"detailContent\": \"Вывод\",\n \"detailPayload\": \"Данные\",\n \"detailExecutionTrace\": \"Трассировка выполнения\",\n \"detailTraceEmpty\": \"Трассировка выполнения недоступна\",\n \"filterByAgent\": \"Фильтр по агенту\",\n \"filterBySourceType\": \"Фильтр по источнику\",\n \"sourceTypeCron\": \"Cron\",\n \"sourceTypeHeartbeat\": \"Heartbeat\",\n \"sourceTypeMemory\": \"Memory\"\n },\n \"codingMode\": {\n \"title\": \"Режим кодирования\",\n \"experimental\": \"Экспериментальная функция\",\n \"experimentalDesc\": \"Режим кодирования превращает QwenPaw в лёгкую Web IDE с редактором кода, деревом файлов, интеграцией Git и AI-помощником для программирования.\",\n \"experimentalNote\": \"Эта функция является экспериментальной и находится в активной разработке. Некоторые возможности могут быть ограничены или изменены в будущих обновлениях.\",\n \"confirmBtn\": \"Включить режим кодирования\",\n \"enterTooltip\": \"Войти в режим кодирования\",\n \"exitTooltip\": \"Выйти из режима кодирования\",\n \"btnCode\": \"Код\",\n \"btnChat\": \"Чат\",\n \"selectProject\": \"Выбрать проект для работы\",\n \"selectProjectDesc\": \"Выберите проект или используйте рабочее пространство по умолчанию\",\n \"tabWorkspace\": \"Рабочее пространство по умолчанию\",\n \"tabClone\": \"Клонировать репозиторий\",\n \"tabLocal\": \"Импортировать локальный проект\",\n \"tabNew\": \"Новый проект\",\n \"workspaceDesc\": \"Использовать директорию рабочего пространства текущего агента\",\n \"cloneUrl\": \"URL Git-репозитория\",\n \"cloneUrlPlaceholder\": \"https://github.com/user/repo.git\",\n \"cloneName\": \"Название проекта (необязательно)\",\n \"cloneNamePlaceholder\": \"Оставьте пустым для определения из URL\",\n \"cloneBtn\": \"Начать клонирование\",\n \"cloning\": \"Клонирование...\",\n \"localPath\": \"Путь к локальной директории\",\n \"localPathPlaceholder\": \"/Users/ваше-имя/мой-проект\",\n \"localPathHint\": \"Введите абсолютный путь к вашему локальному проекту\",\n \"openBtn\": \"Импортировать\",\n \"newName\": \"Название проекта\",\n \"newNamePlaceholder\": \"мой-проект\",\n \"createBtn\": \"Создать проект\",\n \"switchProject\": \"Сменить проект\",\n \"currentProject\": \"Текущий проект\",\n \"resetToWorkspace\": \"Сбросить до рабочего пространства по умолчанию\",\n \"dropPrimary\": \"Перетащите папку сюда или нажмите для выбора\",\n \"dropSecondary\": \"Перетащите из Finder или вставьте путь ниже\",\n \"pathHint\": \"Введите полный абсолютный путь (начинающийся с / или ~)\",\n \"importing\": \"Импорт...\",\n \"dropFailed\": \"Не удалось прочитать папку\",\n \"workingDir\": \"Рабочая директория\",\n \"importCopyNote\": \"Папка будет скопирована в coding_projects/. Оригинальные файлы не будут изменены.\",\n \"importCopyDesc\": \"Импорт создаст копию папки. Агент работает с копией, оригинальные файлы не изменяются. Чтобы работать с оригиналом напрямую, используйте «Открыть каталог».\",\n \"tabOpenDir\": \"Открыть каталог\",\n \"openDirDesc\": \"Открыть существующий локальный каталог — файлы не копируются\",\n \"openDirBtn\": \"Открыть этот каталог\",\n \"openDirLoading\": \"Загрузка...\",\n \"openDirEmpty\": \"Нет подкаталогов\",\n \"openDirHome\": \"Домой\",\n \"openDirRefresh\": \"Обновить\",\n \"openDirHiddenFolders\": \"Скрытые папки\"\n },\n \"market\": {\n \"searchPlaceholder\": \"Поиск навыков по платформам\",\n \"categoryAll\": \"Все\",\n \"categoryPlaceholder\": \"Поиск категории\",\n \"searchResult\": \"Найдено навыков по запросу \\\"{{keyword}}\\\": {{count}}\",\n \"providerUnavailable\": \"Источник недоступен\",\n \"noResults\": \"Ничего не найдено\",\n \"startTyping\": \"Введите запрос выше, чтобы найти навык в маркете\",\n \"browseHint\": \"Выберите категорию или введите ключевое слово, чтобы просмотреть навыки в {{providers}}\",\n \"noDescription\": \"Описание отсутствует\",\n \"browseMarket\": \"Обзор маркета\",\n \"installQueue\": \"Очередь установки\",\n \"clearCompleted\": \"Очистить\",\n \"retry\": \"Повторить\",\n \"status\": {\n \"queued\": \"В очереди\",\n \"installing\": \"Установка\",\n \"completed\": \"Готово\",\n \"failed\": \"Ошибка\",\n \"cancelled\": \"Отменено\"\n },\n \"loadMore\": \"Загрузить ещё\",\n \"noMoreResults\": \"Больше результатов нет\",\n \"detail\": {\n \"title\": \"Сведения о навыке\",\n \"version\": \"Версия\",\n \"author\": \"Автор\",\n \"sourceUrl\": \"URL установки\",\n \"slug\": \"Идентификатор\",\n \"missing\": \"—\"\n },\n \"stats\": {\n \"downloads\": \"Скачивания\",\n \"stars\": \"Звёзды\",\n \"installs\": \"Установки\",\n \"likes\": \"Лайки\",\n \"views\": \"Просмотры\",\n \"category\": \"Категория\",\n \"updatedAt\": \"Обновлено\"\n },\n \"queueMsg\": {\n \"timedOut\": \"Время установки истекло\",\n \"failedPrefix\": \"Ошибка: {{msg}}\"\n }\n },\n \"startup\": {\n \"starting\": \"Запуск сервера...\",\n \"checking\": \"Подключение к серверу...\",\n \"error\": \"Не удалось запустить сервер.\",\n \"errorHint\": \"Не удалось запустить процесс сервера. Проверьте журналы приложения.\",\n \"errorDetails\": \"Показать сведения об ошибке\",\n \"timeout\": \"Не удалось запустить сервер за {{seconds}} секунд.\",\n \"timeoutHint\": \"Не удалось запустить бэкенд. Повторите попытку или проверьте журналы приложения.\",\n \"retry\": \"Повторить\"\n },\n \"preview\": {\n \"error\": {\n \"SENSITIVE_FILE_BLOCKED\": \"Этот файл заблокирован защитой файлов (защита конфиденциальных файлов).\",\n \"OUTSIDE_WORKSPACE\": \"Этот файл находится за пределами рабочего пространства. Включите «Разрешить предпросмотр файлов вне рабочего пространства» в Настройки → Безопасность → Защита файлов.\",\n \"FORBIDDEN\": \"Доступ запрещён: недостаточно прав для предпросмотра этого файла.\",\n \"NOT_FOUND\": \"Файл не найден. Возможно, он был перемещён или удалён. Если использован относительный путь, попробуйте абсолютный.\",\n \"LOAD_FAILED\": \"Не удалось загрузить предпросмотр файла.\",\n \"LOAD_FAILED_DETAIL\": \"Не удалось загрузить предпросмотр файла: {{detail}}\"\n }\n },\n \"tool\": {\n \"readFile\": \"Читать {{file}}\",\n \"readFileDefault\": \"Читать файл\",\n \"writeFile\": \"Создать {{file}}\",\n \"writeFileDefault\": \"Создать файл\",\n \"editFile\": \"Редактировать {{file}}\",\n \"editFileDefault\": \"Редактировать файл\",\n \"appendFile\": \"Добавить в {{file}}\",\n \"appendFileDefault\": \"Добавить в файл\",\n \"grepSearch\": \"Grep {{pattern}}\",\n \"grepSearchDefault\": \"Grep поиск\",\n \"globSearch\": \"Glob {{pattern}}\",\n \"globSearchDefault\": \"Glob поиск\",\n \"viewImage\": \"Просмотр изображения {{file}}\",\n \"viewImageDefault\": \"Просмотр изображения\",\n \"viewVideo\": \"Просмотр видео {{file}}\",\n \"viewVideoDefault\": \"Просмотр видео\",\n \"desktopScreenshot\": \"Снимок рабочего стола\",\n \"sendFile\": \"Отправить {{file}}\",\n \"sendFileDefault\": \"Отправить файл\",\n \"browserUse\": \"Просмотр {{detail}}\",\n \"browserNavigate\": \"Открыть {{url}}\",\n \"browserNavigateDefault\": \"Открыть страницу\",\n \"browserClick\": \"Нажать\",\n \"browserType\": \"Ввести текст\",\n \"browserSnapshot\": \"Снимок страницы\",\n \"browserScroll\": \"Прокрутка\",\n \"getCurrentTime\": \"Получить текущее время\",\n \"setTimezone\": \"Установить {{timezone}}\",\n \"getTokenUsage\": \"Запросить использование токенов\",\n \"memorySearch\": \"Поиск в памяти {{query}}\",\n \"memorySearchDefault\": \"Поиск в памяти\",\n \"listAgents\": \"Список агентов\",\n \"chatWithAgent\": \"Чат с {{agent}}\",\n \"chatWithAgentDefault\": \"Чат с агентом\",\n \"submitToAgent\": \"Отправить {{agent}}{{task}}\",\n \"submitToAgentDefault\": \"Отправить задачу\",\n \"checkAgentTask\": \"Проверить {{agent}} #{{taskId}}\",\n \"checkAgentTaskAgent\": \"Проверить {{agent}}\",\n \"checkAgentTaskDefault\": \"Проверить задачу\",\n \"delegateExternalAgent\": \"Делегировать {{runner}}\",\n \"delegateExternalAgentDefault\": \"Делегировать внешнему агенту\",\n \"materializeSkill\": \"Создать {{skill}}\",\n \"materializeSkillDefault\": \"Создать навык\",\n \"shell\": \"Выполнить {{command}}\",\n \"shellDefault\": \"Выполнить команду\",\n \"formatTable\": {\n \"file\": \"Файл\",\n \"lineNumber\": \"Строка\",\n \"score\": \"Оценка\",\n \"summary\": \"Сводка\",\n \"name\": \"Имя\",\n \"id\": \"ID\",\n \"description\": \"Описание\",\n \"status\": \"Статус\"\n },\n \"loading\": \"Загрузка...\",\n \"inputProgress\": \"Получено символов: {{count}}\",\n \"rawInputPreview\": \"Необработанный ввод в реальном времени (последние 4 096 символов)\",\n \"largeOutputOmitted\": \"Пропущено строк: {{count}} — скопируйте доступный вывод или следуйте инструкциям по восстановлению\",\n \"largeOutputTruncated\": \"Вывод усечён для отображения — скопируйте доступный вывод или следуйте инструкциям по восстановлению\",\n \"browserAction\": {\n \"startHeaded\": \"Запустить браузер (с интерфейсом)\",\n \"start\": \"Запустить браузер\",\n \"stop\": \"Остановить браузер\",\n \"open\": \"Открыть {{url}}\",\n \"openDefault\": \"Открыть страницу\",\n \"navigate\": \"Перейти на {{url}}\",\n \"navigateDefault\": \"Навигация\",\n \"navigateBack\": \"Назад\",\n \"click\": \"Нажать {{selector}}\",\n \"clickDefault\": \"Нажать элемент\",\n \"type\": \"Ввести {{text}}\",\n \"typeDefault\": \"Ввести текст\",\n \"snapshot\": \"Получить снимок\",\n \"screenshot\": \"Снимок экрана {{path}}\",\n \"screenshotDefault\": \"Снимок экрана\",\n \"eval\": \"Выполнить {{code}}\",\n \"evalDefault\": \"Выполнить код\",\n \"runCode\": \"Запустить {{code}}\",\n \"runCodeDefault\": \"Запустить код\",\n \"closePage\": \"Закрыть страницу\",\n \"tabs\": \"Вкладки {{action}}\",\n \"tabsDefault\": \"Управление вкладками\",\n \"fillForm\": \"Заполнить форму\",\n \"fileUpload\": \"Загрузить {{filename}}\",\n \"fileUploadDefault\": \"Загрузить файл\",\n \"fileDownload\": \"Скачать {{target}}\",\n \"fileDownloadDefault\": \"Скачать файл\",\n \"pressKey\": \"Нажать {{key}}\",\n \"pressKeyDefault\": \"Нажать клавишу\",\n \"hover\": \"Навести на {{selector}}\",\n \"hoverDefault\": \"Навести на элемент\",\n \"drag\": \"Перетащить\",\n \"selectOption\": \"Выбрать опцию\",\n \"waitFor\": \"Ждать {{target}}\",\n \"waitForDefault\": \"Ждать элемент\",\n \"resize\": \"Изменить размер {{w}}x{{h}}\",\n \"resizeDefault\": \"Изменить размер окна\",\n \"pdf\": \"Экспорт PDF {{path}}\",\n \"pdfDefault\": \"Экспорт PDF\",\n \"install\": \"Установить\",\n \"batch\": \"Пакет\"\n },\n \"lineBadge\": {\n \"lines\": \"{{count}} строк\",\n \"addLines\": \"+{{count}} строк\",\n \"delLines\": \"-{{count}} строк\",\n \"matches\": \"{{count}} совпадений\",\n \"files\": \"{{count}} файлов\"\n },\n \"inlineResult\": {\n \"taskId\": \"Задача #{{id}}\",\n \"submitted\": \"Отправлено\"\n },\n \"execute\": \"Запустить {{tool}}\",\n \"executeDefault\": \"Запустить инструмент\"\n },\n \"loop\": {\n \"gotoSettings\": \"Настройки Loop агента\",\n \"selectorTitle\": \"Режим Loop\",\n \"selectorHint\": \"Выберите способ продолжения работы агента\",\n \"selectorAria\": \"Выбрать режим Loop\",\n \"builtInModes\": \"Встроенные\",\n \"customModes\": \"Пользовательские и плагины\",\n \"starting\": \"Запуск\",\n \"running\": \"Выполняется\",\n \"awaiting_user\": \"Ожидание вашего ответа\",\n \"activePersistentDescription\": \"Режим действует в этом диалоге до завершения задачи или сброса диалога.\",\n \"activeCustomDescription\": \"Пользовательский режим действует до команды /mode off, /clear или /new.\",\n \"loadError\": \"Не удалось загрузить некоторые режимы Loop.\",\n \"retry\": \"Повторить\",\n \"modes\": {\n \"default\": {\n \"name\": \"По умолчанию\",\n \"description\": \"Стандартный управляемый цикл агента.\"\n },\n \"goal\": {\n \"name\": \"Цель\",\n \"description\": \"Последовательная работа над конкретной проверяемой целью.\"\n },\n \"mission\": {\n \"name\": \"Миссия\",\n \"description\": \"Структурированная длительная многоэтапная задача.\"\n }\n }\n }\n}\n"}}, "console/src/locales/pt-BR.json": {"db8fb6391097791b08f63623ba4cd88421ec5cdb": {"t": 1785846849.5721705, "n": 185902, "h": "ba845cd8354f970d", "c": "{\n \"common\": {\n \"save\": \"Salvar\",\n \"reset\": \"Redefinir\",\n \"cancel\": \"Cancelar\",\n \"confirm\": \"Confirmar\",\n \"delete\": \"Excluir\",\n \"edit\": \"Editar\",\n \"create\": \"Criar\",\n \"upload\": \"Enviar\",\n \"download\": \"Baixar\",\n \"refresh\": \"Atualizar\",\n \"search\": \"Pesquisar\",\n \"enable\": \"Ativar\",\n \"disable\": \"Desativar\",\n \"enabled\": \"Ativado\",\n \"disabled\": \"Desativado\",\n \"preview\": \"Visualizar\",\n \"view\": \"Visualizar\",\n \"content\": \"Conteudo\",\n \"loading\": \"Carregando...\",\n \"saving\": \"Salvando...\",\n \"copy\": \"Copiar\",\n \"copied\": \"Copiado para a area de transferencia\",\n \"copyFailed\": \"Falha ao copiar para a area de transferencia\",\n \"contentPlaceholder\": \"Digite o conteudo...\",\n \"help\": \"Ajuda\",\n \"close\": \"Fechar\",\n \"actions\": \"Acoes\",\n \"total\": \"Total {{count}}\",\n \"clear\": \"Limpar\",\n \"paste\": \"Colar\",\n \"yes\": \"Sim\",\n \"no\": \"Nao\"\n },\n \"harnesses\": {\n \"connected\": \"Conectado\",\n \"disconnected\": \"Desconectado\",\n \"notConnected\": \"Não conectado\",\n \"comingSoon\": \"Em breve\",\n \"connect\": \"Conectar com o ChatGPT\",\n \"disconnect\": \"Desconectar\",\n \"apiKeyAuthenticated\": \"Autenticado com chave de API\",\n \"chatGptAuthenticated\": \"Conectado ao ChatGPT\",\n \"cliAuthenticated\": \"{{type}} CLI autenticado\"\n },\n \"desktop\": {\n \"closeWindow\": {\n \"title\": \"Fechar Janela\",\n \"description\": \"O que deseja fazer ao fechar a janela? Sair do aplicativo interrompe todas as tarefas em execução e tarefas agendadas.\",\n \"remember\": \"Lembrar minha escolha\",\n \"showWindow\": \"Mostrar janela\",\n \"minimizeToTray\": \"Minimizar para a bandeja\",\n \"quitApp\": \"Sair do aplicativo\",\n \"preference\": \"Fechar Janela\",\n \"askEveryTime\": \"Perguntar sempre\"\n }\n },\n \"chunkError\": {\n \"title\": \"Falha ao carregar pagina\",\n \"subTitle\": \"This may be caused by a network issue or an application update.\",\n \"genericTitle\": \"Algo deu errado\",\n \"genericSubTitle\": \"An unexpected error occurred while rendering this page.\",\n \"reload\": \"Recarregar\"\n },\n \"nav\": {\n \"chat\": \"Chat\",\n \"control\": \"Controle\",\n \"channels\": \"Canais\",\n \"sessions\": \"Sessoes\",\n \"cronJobs\": \"Tarefas Cron\",\n \"heartbeat\": \"Heartbeat\",\n \"agent\": \"Workspace\",\n \"workspace\": \"Arquivos\",\n \"skills\": \"Skills\",\n \"skillPool\": \"Pool de Skills\",\n \"tools\": \"Ferramentas\",\n \"mcp\": \"MCP\",\n \"acp\": \"ACP\",\n \"agentConfig\": \"Configuracao\",\n \"agents\": \"Gerenciamento de Agentes\",\n \"settings\": \"Configuracoes\",\n \"models\": \"Modelos\",\n \"environments\": \"Environments\",\n \"security\": \"Seguranca\",\n \"tokenUsage\": \"Uso de Tokens\",\n \"agentStats\": \"Estatisticas dos Agentes\",\n \"voiceTranscription\": \"Transcricao de Voz\",\n \"debug\": \"Depuracao\",\n \"plugins\": \"Plugins\",\n \"backups\": \"Backups\",\n \"pluginManager\": \"Gerenciador de Plugins\"\n },\n \"debug\": {\n \"title\": \"Depuracao\",\n \"desc\": \"View the backend daemon log file to help diagnose issues. Logs refresh automatically while this page is open.\",\n \"level\": {\n \"all\": \"Todos\"\n },\n \"backend\": {\n \"title\": \"Logs do backend\",\n \"autoRefresh\": \"Atualizacao automatica\",\n \"newestFirst\": \"Mais recentes primeiro\",\n \"updatedAt\": \"Atualizado em\",\n \"path\": \"Arquivo de log\",\n \"notFound\": \"Backend log file was not found yet.\",\n \"placeholder\": \"Backend log output will appear here.\",\n \"loadFailed\": \"Falha ao load Backend Logs\",\n \"searchPlaceholder\": \"Pesquisar logs do backend...\"\n },\n \"actions\": {\n \"refreshBackend\": \"Atualizar logs do backend\",\n \"refreshSuccess\": \"Logs atualizados\",\n \"copyBackend\": \"Copiar logs do backend\"\n }\n },\n \"acp\": {\n \"title\": \"ACP\",\n \"loading\": \"Carregando configuracao ACP...\",\n \"builtin\": \"Integrado\",\n \"custom\": \"Personalizado\",\n \"create\": \"Adicionar Personalizado Agente\",\n \"createTitle\": \"Criar ACP Agente\",\n \"createSuccess\": \"Agente ACP criado\",\n \"editTitle\": \"Editar ACP Configuracao\",\n \"agentKey\": \"Chave do Agente\",\n \"agentKeyRequired\": \"Digite a chave do agente\",\n \"agentKeyInvalid\": \"A chave pode conter apenas letras, numeros, sublinhados e hifens\",\n \"agentKeyExists\": \"Esta chave de agente ja existe\",\n \"enabled\": \"Ativado\",\n \"command\": \"Command\",\n \"commandRequired\": \"Digite um comando\",\n \"args\": \"Arguments\",\n \"argsHelp\": \"Um argumento por linha\",\n \"env\": \"Variaveis de Ambiente\",\n \"envHelp\": \"Use o formato CHAVE=VALOR em cada linha\",\n \"envInvalidLine\": \"Formato de variavel de ambiente invalido: {{line}}\",\n \"trusted\": \"Trusted\",\n \"toolParseMode\": \"Modo de Parse de Ferramentas\",\n \"toolParseModeRequired\": \"Selecione um modo de parse\",\n \"stdioBufferLimit\": \"Limite do Buffer Stdio\",\n \"stdioBufferLimitHelp\": \"Tamanho maximo do buffer de linha stdio para o subprocesso ACP em bytes.\",\n \"stdioBufferLimitRequired\": \"Digite o limite do buffer stdio\",\n \"stdioBufferLimitMin\": \"O limite deve ser de pelo menos 1 byte\",\n \"docs\": \"Documentacao de Setup\",\n \"docsHelp\": \"Abrir a documentacao ACP e ir para a secao de configuracao de runners externos\",\n \"notSet\": \"Nao definido\",\n \"configSaved\": \"Configuracao ACP salva\",\n \"configFailed\": \"Falha ao salvar configuracao ACP\",\n \"deleteTitle\": \"Excluir {{name}}\",\n \"deleteConfirm\": \"Excluir este agente ACP? Esta acao nao pode ser desfeita.\",\n \"deleteSuccess\": \"Agente ACP excluido\",\n \"deleteFailed\": \"Falha ao excluir agente ACP\",\n \"nodeSettings\": \"Configuracoes do Node\",\n \"nodePath\": \"Caminho do Node\",\n \"chooseOtherNode\": \"Escolher outro Node...\",\n \"selectNodePath\": \"Selecionar caminho do Node\",\n \"nodePathPrompt\": \"Informe o caminho do executavel node\",\n \"nodeSaved\": \"Configuracoes do Node salvas\",\n \"nodeLoadFailed\": \"Falha ao carregar configuracoes do Node\",\n \"nodeSaveFailed\": \"Caminho do Node indisponivel\",\n \"nodeRuntime\": {\n \"bundled\": \"Node embutido\",\n \"system\": \"Node do sistema\",\n \"custom\": \"Node personalizado\"\n },\n \"nodeRuntimeReason\": {\n \"systemNodeMissing\": \"Node do sistema nao detectado\",\n \"nodeMissing\": \"Caminho do Node nao existe\",\n \"npxMissing\": \"npx nao encontrado\",\n \"versionCheckFailed\": \"Falha na verificacao de versao\",\n \"unavailable\": \"Indisponivel\"\n }\n },\n \"voiceTranscription\": {\n \"title\": \"Transcricao de Voz\",\n \"description\": \"Configure como mensagens de audio e voz recebidas sao processadas.\",\n \"saveSuccess\": \"Modo de audio salvo\",\n \"saveFailed\": \"Falha ao salvar modo de audio\",\n \"audioModeLabel\": \"Modo de Audio\",\n \"audioModeDescription\": \"Escolha como mensagens de voz dos canais (Discord, Telegram, etc.) sao processadas antes de serem enviadas ao modelo.\",\n \"modeAuto\": \"Auto (Recomendado)\",\n \"modeAutoDesc\": \"Transcribe audio to text using the selected transcription provider, then send the text to the model. If transcription is unavailable or disabled, a file-uploaded placeholder is shown instead. Audio is never sent directly to the model in this mode. Works with all models.\",\n \"modeNative\": \"Audio Nativo\",\n \"modeNativeDesc\": \"Send the audio file directly to the model without transcription. This is the only mode that sends audio to the model. Only works with specific audio-capable models (e.g. gpt-4o-audio). Most models do not support this and will reject the message.\",\n \"ffmpegReady\": \"ffmpeg is installed. Audio conversion is available for native mode.\",\n \"ffmpegMissing\": \"ffmpeg is not installed.\",\n \"ffmpegMissingDesc\": \"Native audio mode requires ffmpeg to convert audio formats (e.g. .ogg to .wav). Install ffmpeg as a system package to enable this mode.\",\n \"providerTypeLabel\": \"Provedor de Transcricao\",\n \"providerTypeDescription\": \"Choose the transcription backend. Select Disabled if you do not need voice transcription.\",\n \"providerTypeDisabled\": \"Desativado\",\n \"providerTypeDisabledDesc\": \"No transcription. Voice messages will show a file-uploaded placeholder.\",\n \"providerTypeWhisperApi\": \"Whisper API\",\n \"providerTypeWhisperApiDesc\": \"Use an OpenAI-compatible Whisper API endpoint from a configured provider (e.g. OpenAI, Ollama).\",\n \"providerTypeLocalWhisper\": \"Local Whisper\",\n \"providerTypeLocalWhisperDesc\": \"Run transcription locally using the openai-whisper Python library. Requires both ffmpeg and openai-whisper to be installed.\",\n \"localWhisperReady\": \"Local Whisper is ready. Both ffmpeg and openai-whisper are installed.\",\n \"localWhisperMissing\": \"Local Whisper is not ready. Missing dependencies must be installed.\",\n \"localWhisperMissingDesc\": \"ffmpeg: {{ffmpeg}} | openai-whisper: {{whisper}}. Install missing dependencies: ffmpeg (system package) and openai-whisper (uv pip install openai-whisper, or install QwenPaw with the [whisper] extra).\",\n \"providerLabel\": \"Whisper API Provedor\",\n \"providerDescription\": \"Select which provider to use for audio transcription via the Whisper API. Only providers with a Whisper-compatible endpoint are shown.\",\n \"providerPlaceholder\": \"Selecionar a provider...\",\n \"noProvidersWarning\": \"No transcription-capable providers found. Configure an OpenAI provider to enable voice transcription.\",\n \"transcriptionInfoTitle\": \"How transcription works\",\n \"transcriptionInfoDesc\": \"Whisper API transcription uses an OpenAI-compatible /v1/audio/transcriptions endpoint. This requires a configured provider with a Whisper-compatible endpoint — for example, an OpenAI provider. Select a specific provider above to enable transcription.\",\n \"transcriptionInfoDescLocal\": \"Local Whisper transcription runs the openai-whisper library directly on your machine. It requires both ffmpeg (for audio decoding) and the openai-whisper Python package to be installed. No API key or network connection is needed. Install with: uv pip install 'qwenpaw[whisper]'.\"\n },\n \"backup\": {\n \"title\": \"Backups\",\n \"description\": \"Backup and restore agent workspaces and configurations.\",\n \"create\": \"Criar Backup\",\n \"import\": \"Importar\",\n \"export\": \"Exportar\",\n \"restore\": \"Restore\",\n \"delete\": \"Excluir\",\n \"batchDelete\": \"Excluir Selecionado\",\n \"batchDeleteConfirm\": \"Are you sure you want to delete {{count}} backup(s)? This action cannot be undone.\",\n \"deleteConfirm\": \"Are you sure you want to delete this backup? This action cannot be undone.\",\n \"name\": \"Nome\",\n \"createdAt\": \"Criado\",\n \"scopeSummary\": \"Escopo\",\n \"descriptionLabel\": \"Descricao\",\n \"noBackups\": \"No backups yet. Create one to backup your current configuration.\",\n \"loadFailed\": \"Falhou to load Backups\",\n \"createTitle\": \"Criar Backup\",\n \"backupMode\": \"Backup Modo\",\n \"fullBackup\": \"Full Backup\",\n \"fullBackupDesc\": \"Backup everything including all agent workspaces, global settings, skill pool, and secrets\",\n \"customBackup\": \"Personalizado Backup\",\n \"partialBackup\": \"Partial Backup\",\n \"partialBackupDesc\": \"Customize what to backup\",\n \"nameRequired\": \"Backup Nome is Obrigatorio\",\n \"namePlaceholder\": \"Enter backup Nome\",\n \"descriptionPlaceholder\": \"Opcional description...\",\n \"scopeAgents\": \"Agente Workspaces\",\n \"scopeGlobalConfig\": \"Global Configuracoes\",\n \"scopeSkillPool\": \"Pool de Skills\",\n \"scopeSecrets\": \"Secrets\",\n \"scopeSecretsHint\": \"Includes model provider secrets (API keys), environment variables, and other sensitive information\",\n \"securityNotice\": \"This backup may contain sensitive credentials. Agent workspaces include channel credentials (bot tokens, app secrets, etc.), and model provider secrets include API keys. Do not share backup files with others.\",\n \"searchPlaceholder\": \"Search backups by name or ID...\",\n \"total\": \"Total {{count}}\",\n \"agentsPlaceholder\": \"Select agent workspaces (leave empty for all)\",\n \"selectAll\": \"Selecionar Todos\",\n \"deselectAll\": \"Deselect Todos\",\n \"createSuccess\": \"Backup Criado successfully\",\n \"createFailed\": \"Falhou to Criar backup\",\n \"restoreTitle\": \"Restore Backup\",\n \"restoreScope\": \"Restore Escopo\",\n \"restoreFullWarning\": \"Full restore will completely replace all contents of the current instance (including all agents, global config, skill pool, and secrets). This action cannot be undone. Related features will be unavailable during restore. Please restart the service after restoration is complete.\",\n \"agentGroupExisting\": \"Existing\",\n \"agentGroupNew\": \"Novo\",\n \"agentExists\": \"Exists\",\n \"agentNew\": \"Novo\",\n \"agentActionReplace\": \"Overwrite\",\n \"agentActionAdd\": \"Novo\",\n \"agentColumnName\": \"Agente\",\n \"agentColumnWorkspace\": \"Workspace\",\n \"agentTotal\": \"{{count}} Agentes total\",\n \"noAgentsInBackup\": \"No agent workspaces in this backup\",\n \"loadingAgents\": \"Carregando Agente list...\",\n \"agentSearchPlaceholder\": \"Search by name or ID...\",\n \"agentSearchTotal\": \"{{count}} of {{total}} Agentes\",\n \"restoreCustomSummary\": \"{{existing}} existing · {{added}} new selected\",\n \"defaultWorkspaceDirUnused\": \"Nao Novo Agentes Selecionado\",\n \"otherOptions\": \"Other Options\",\n \"defaultWorkspaceDirDefault\": \"<Default path>/{{aid}}\",\n \"restoreWarningModify\": \"Restoring will modify current configurations. This action cannot be undone.\",\n \"restoreWarningRestart\": \"Related features will be unavailable during restore. Please restart the service after restoration is complete.\",\n \"preRestoreBackupTitle\": \"Criar Pre-Restore Backup\",\n \"preRestoreBackupContent\": \"The restore operation is irreversible. Would you like to create a backup of the current state first? This allows you to rollback if any issues occur during restoration.\",\n \"preRestoreBackupYes\": \"Yes, Criar backup first\",\n \"preRestoreBackupNo\": \"No, restore directly\",\n \"preRestoreBackupDesc\": \"Auto-created backup before restore\",\n \"preRestoreBackupFailed\": \"Falha ao Criar pre-restore backup\",\n \"preRestoreBackupSuccess\": \"Pre-restore backup created successfully, ID: {{id}}\",\n \"creatingPreRestoreBackup\": \"Creating pre-restore backup...\",\n \"skipAndRestore\": \"Skip and continue to restore\",\n \"restoreConfirm\": \"I confirm that I want to restore this backup\",\n \"restoreMode\": \"Restore Modo\",\n \"restoreModeFull\": \"Full Restore\",\n \"restoreModeFullDesc\": \"Completely replace all contents of the current instance, including all agents, global config, skill pool and secrets\",\n \"restoreModeCustom\": \"Personalizado Restore\",\n \"restoreModeCustomDesc\": \"Select the contents to restore, such as specific agents, config, etc.; existing agents not included in this restore will be preserved\",\n \"restoreModeFullDisabled\": \"Full backup only\",\n \"defaultWorkspaceDir\": \"Agente Padrao Workspace Directory\",\n \"defaultWorkspaceDirPlaceholder\": \"Leave empty to use ~/.qwenpaw/workspaces\",\n \"defaultWorkspaceDirHint\": \"If the agent's original workspace path does not exist, the agent will be saved under <default path>/<agent ID>. If it exists, the agent's original workspace path will be used.\",\n \"restoreStrategy\": \"Estratégia de restauração\",\n \"restoreStrategyPreserve\": \"Preservar segurança local e MCP\",\n \"restoreStrategyPreserveDesc\": \"Mantenha as proteções de segurança e a configuração MCP desta instância.\",\n \"restoreStrategyRestore\": \"Restaurar estas configurações do backup\",\n \"restoreStrategyRestoreDesc\": \"Use a configuração de segurança e MCP do backup.\",\n \"restoreSuccess\": \"Backup restored successfully. Please restart the service.\",\n \"restoreSuccessPreserved\": \"Backup restaurado com sucesso. Configurações locais preservadas: {{keys}}. Reinicie o serviço.\",\n \"restoreSuccessWithBackup\": \"Backup restored successfully. Pre-restore backup ID: {{preRestoreId}}. Please restart the service.\",\n \"restoreFailed\": \"Falhou to restore backup\",\n \"restoreTargetBusy\": \"A restauração falhou porque estes diretórios ainda estão em uso. Feche o navegador ou processo que os utiliza e tente novamente. Se continuarem bloqueados, reinicie o sistema e tente de novo.\",\n \"detailLoadFailed\": \"Failed to load backup details; restore is unavailable\",\n \"trustLocalBanner\": \"Backup local - restauração completa por padrão\",\n \"trustForeignBanner\": \"Backup importado - segurança local e MCP são preservados por padrão\",\n \"trustLegacyBanner\": \"Backup legado - confirmação de confiança necessária antes da restauração\",\n \"trustForeignTitle\": \"Confiar neste backup?\",\n \"trustLegacyTitle\": \"Confiar no backup legado?\",\n \"unknownBackupName\": \"Arquivo de backup\",\n \"trustForeignDesc\": \"Este backup não foi assinado por esta instância. Continue apenas se confiar na origem; as configurações locais de segurança e MCP serão preservadas por padrão ao restaurar.\",\n \"trustLegacyDesc\": \"Este backup antigo não tem assinatura local. Continue apenas se confiar na origem; esta instância o assinará antes da restauração.\",\n \"deleteSuccess\": \"Backup(s) deleted successfully\",\n \"deleteFailed\": \"Falhou to Excluir backup(s)\",\n \"exportFailed\": \"Falhou to Exportar backup\",\n \"exportWarningTitle\": \"Sensitive Information Aviso\",\n \"exportWarningContent\": \"This backup may contain sensitive credential information. Agent workspaces contain channel credentials (such as bot tokens, app secrets, etc.), and model provider secrets contain API Keys. Do not share backup files with others.\",\n \"exportConfirm\": \"Confirmar Exportar\",\n \"importTitle\": \"Importar Backup\",\n \"importSuccess\": \"Backup imported successfully\",\n \"importFailed\": \"Falhou to Importar backup\",\n \"importConflictTitle\": \"Backup Already Exists\",\n \"importConflictDesc\": \"A backup with the same ID already exists. Do you want to overwrite it?\",\n \"importReplace\": \"Overwrite\",\n \"agents\": \"{{count}} Agent(s)\",\n \"allAgents\": \"Todos Agentes\",\n \"globalConfig\": \"Global Configuracoes\",\n \"skillPool\": \"Pool de Skills\",\n \"secrets\": \"Secrets\",\n \"selected\": \"{{count}} Selecionado\",\n \"fileCount\": \"{{count}} Arquivos\",\n \"totalSize\": \"{{size}}\",\n \"progressStarting\": \"Starting backup...\",\n \"progressAgent\": \"Backing up agent {{index}} / {{total}}...\",\n \"progressSaving\": \"Saving backup file...\",\n \"progressDone\": \"Backup complete\",\n \"localModelsNotice\": \"Backup files do not include local model files. For cross-device migration, you will need to re-download the required local models on the target device.\"\n },\n \"checkpoints\": {\n \"nav\": \"Pontos de controle\",\n \"title\": \"Pontos de controle de estado\",\n \"auto\": \"Automático\",\n \"autoEnabled\": \"Pontos automáticos ativados\",\n \"autoDisabled\": \"Pontos automáticos desativados\",\n \"refresh\": \"Atualizar\",\n \"snapshot\": \"Snapshot\",\n \"snapshotCreated\": \"Snapshot criado\",\n \"graph\": \"Grafo\",\n \"checkpoint\": \"Ponto de controle\",\n \"type\": \"Tipo\",\n \"commit\": \"Commit\",\n \"createdAt\": \"Criado em\",\n \"session\": \"Sessão\",\n \"channel\": \"Canal\",\n \"parent\": \"Pai\",\n \"details\": \"Detalhes\",\n \"search\": \"Pesquisar consulta, nome, sessão ou SHA\",\n \"allTypes\": \"Todos os tipos\",\n \"allSessions\": \"Todas as sessões\",\n \"showingLatest\": \"Exibindo os {{count}} mais recentes\",\n \"loadFailed\": \"Não foi possível carregar\",\n \"retry\": \"Tentar novamente\",\n \"more\": \"Mais ações\",\n \"noMatches\": \"Nenhum ponto corresponde aos filtros.\",\n \"empty\": \"Ainda não há pontos de controle. O histórico aparecerá aqui.\",\n \"kind\": {\n \"auto\": \"Auto\",\n \"snapshot\": \"Snapshot\",\n \"safety\": \"Segurança\",\n \"commit\": \"Commit\"\n },\n \"summary\": { \"total\": \"pontos\" },\n \"snapshotDialog\": {\n \"title\": \"Criar snapshot\",\n \"session\": \"Sessão\",\n \"name\": \"Nome\",\n \"placeholder\": \"Nome opcional\"\n },\n \"gc\": {\n \"settingsAction\": \"Configurações de limpeza automática\",\n \"settingsTitle\": \"Configurações de limpeza automática\",\n \"keepCount\": \"Pontos automáticos a manter\",\n \"keepDays\": \"Retenção de pontos automáticos (dias)\",\n \"preRestoreDays\": \"Retenção de snapshots pré-restauração (dias)\",\n \"settingsSaved\": \"Configurações de limpeza automática salvas\",\n \"thoroughAction\": \"Thoroughly compact checkpoints\",\n \"thoroughTitle\": \"Thoroughly compact checkpoints?\",\n \"thoroughDescription\": \"Remove {{count}} ponto(s), ignorando a retenção de pontos automáticos. O HEAD de cada sessão será mantido.\",\n \"thoroughConfirm\": \"Thoroughly compact\",\n \"thoroughSuccess\": \"Removed {{count}} checkpoint(s) during thorough compaction\",\n \"action\": \"Limpar pontos\",\n \"title\": \"Limpar pontos conforme a política de retenção?\",\n \"description\": \"Remove {{count}} ponto(s) expirado(s) conforme a política de retenção atual. O HEAD de cada sessão será mantido.\",\n \"confirm\": \"Limpar\",\n \"success\": \"{{count}} ponto(s) removidos\"\n },\n \"reset\": {\n \"action\": \"Redefinir dados\",\n \"title\": \"Redefinir todos os dados?\",\n \"description\": \"Isso remove permanentemente todos os pontos e desativa a criação automática.\",\n \"confirm\": \"Redefinir\",\n \"success\": \"Dados redefinidos\"\n },\n \"restore\": {\n \"action\": \"Restaurar\",\n \"title\": \"Restaurar ponto\",\n \"preview\": \"Visualizar restauração\",\n \"confirm\": \"Restaurar\",\n \"conversation\": \"Conversa\",\n \"memory\": \"Memória\",\n \"files\": \"Arquivos do workspace\",\n \"refreshWarning\": \"A conversa será alterada. Atualize-a após a restauração.\",\n \"selectedCount\": \"{{count}} arquivos selecionados\",\n \"selectAll\": \"Selecionar todos os arquivos alterados\",\n \"delete\": \"Excluir\",\n \"restore\": \"Restaurar\",\n \"noFileChanges\": \"Nenhuma alteração de arquivo é necessária.\",\n \"success\": \"Ponto restaurado. Atualize a conversa para carregar o estado.\"\n }\n },\n \"workspace\": {\n \"title\": \"Arquivos\",\n \"workspacePath\": \"Workspace:\",\n \"noFiles\": \"Nao Arquivos\",\n \"coreFiles\": \"Core Arquivos\",\n \"coreFilesDesc\": \"Bootstrap persona, identity, and tool guidance.\",\n \"uploadTooltip\": \"ZIP files only\",\n \"uploadTooltipWithLimit\": \"ZIP files only, max {{limit}}MB\",\n \"selectFile\": \"Selecione um arquivo para editar\",\n \"fileContent\": \"File content...\",\n \"uploadSuccess\": \"Arquivo enviado com sucesso\",\n \"uploadFailed\": \"Falha ao enviar arquivo\",\n \"downloadPreparing\": \"Preparando download do workspace...\",\n \"downloadSuccess\": \"Workspace downloaded successfully\",\n \"downloadFailed\": \"Workspace Baixar Falhou\",\n \"zipOnly\": \"Only .zip files are supported for upload\",\n \"fileSizeExceeded\": \"File size exceeds {{limit}}MB limit. Current file: {{size}}MB\",\n \"systemPromptToggleTooltip\": \"Enable/disable this file in system prompt\",\n \"memoryFileWarning\": \"MEMORY.md is typically queried on-demand by the agent using tools. Loading it into the system prompt may cause the context to become too long.\",\n \"configUpdated\": \"System prompt Configuracao Atualizado\",\n \"configUpdateFailed\": \"Falha ao Atualizar system prompt Configuracao\",\n \"attribution\": \"Workspace design partly inspired by the OpenClaw project — thank you! 🐾\",\n \"saveSuccess\": \"Salvo com sucesso\",\n \"saveFailed\": \"Falha ao salvar\"\n },\n \"agent\": {\n \"parent\": \"Configuracoes\",\n \"agents\": \"Agentes\",\n \"management\": \"Gerenciamento de Agentes\",\n \"pageDescription\": \"Create, configure, and manage multiple AI agents with custom workspaces and identities.\",\n \"name\": \"Nome\",\n \"id\": \"ID\",\n \"description\": \"Descricao\",\n \"workspace\": \"Caminho do Workspace\",\n \"backend\": {\n \"column\": \"Backend\",\n \"eyebrow\": \"Arquitetura do agente\",\n \"typeTitle\": \"Escolha como este agente funciona\",\n \"typeDescription\": \"O tipo de agente controla todas as conversas no Chat, Coding e Channel.\",\n \"nativeTitle\": \"Agente nativo do QwenPaw\",\n \"nativeBadge\": \"Nativo\",\n \"nativeDescription\": \"Usa o LLM, Skills, ferramentas, memória e runtime do QwenPaw configurados.\",\n \"thirdPartyTitle\": \"Agente de terceiros\",\n \"thirdPartyBadge\": \"Terceiros\",\n \"thirdPartyDescription\": \"Usa um runtime de agente externo autorizado em vez de um modelo do QwenPaw.\",\n \"providerTitle\": \"Escolha um agente de terceiros\",\n \"providerDescription\": \"A autenticação é compartilhada neste dispositivo; projetos e conversas permanecem isolados por agente.\",\n \"codexHint\": \"ChatGPT OAuth · Workspace do agente\",\n \"qoderHint\": \"Conta Qoder ou PAT · Workspace do agente\",\n \"account\": \"Conta do Codex\",\n \"qoderAccount\": \"Conta do Qoder\",\n \"binary\": \"Executável do Codex\",\n \"binaryHelp\": \"Deixe em branco para priorizar o Codex CLI incluído no SDK Python e detectar uma instalação independente. Se preencher, informe o caminho completo para codex no macOS/Linux ou codex.exe no Windows. Binários internos do aplicativo ChatGPT ou de extensões do editor não são compatíveis. O QwenPaw também lê CODEX_BINARY e o PATH do backend.\",\n \"binaryPlaceholder\": \"Detectar automaticamente ou colar o caminho do executável Codex\",\n \"qoderBinary\": \"Executável do Qoder\",\n \"qoderBinaryHelp\": \"Deixe em branco para usar o qodercli incluído no SDK Python ou detectar uma instalação existente. Se preencher, informe o caminho completo para qodercli no macOS/Linux ou qodercli.exe no Windows. O QwenPaw também lê QODERCLI_PATH e o PATH do backend.\",\n \"qoderBinaryPlaceholder\": \"Detectar automaticamente ou colar o caminho do qodercli\",\n \"detect\": \"Detectar\",\n \"detectedBinary\": \"Codex detectado\",\n \"qoderDetectedBinary\": \"Qoder detectado\",\n \"apiKeyLoginHint\": \"Você também pode autenticar no terminal com: codex login --with-api-key\",\n \"qoderAuthHint\": \"Conectar abre o login do Qoder no navegador. Você também pode executar qodercli login ou definir QODER_PERSONAL_ACCESS_TOKEN antes de iniciar o backend.\",\n \"qoderConnect\": \"Conectar ao Qoder\",\n \"model\": \"Modelo do agente\",\n \"modelDefault\": \"Usar o padrão do agente\",\n \"defaultBadge\": \"Padrão\",\n \"reasoningEffort\": \"Nível de raciocínio\",\n \"reasoningDefault\": \"Usar o padrão do modelo\",\n \"codexNotFound\": \"O runtime do Codex não foi encontrado. Instale qwenpaw[codex] ou forneça um Codex CLI independente.\",\n \"qoderNotFound\": \"O runtime do Qoder não foi encontrado. Instale qwenpaw[qoder] ou forneça o qodercli.\",\n \"chatSettingsHint\": \"Escolha o modelo, o nível de raciocínio e as permissões de execução na barra de ferramentas do Chat.\",\n \"capabilityTitle\": \"Recursos do QwenPaw\",\n \"inheritedSkills\": \"Skills\",\n \"inheritedMcp\": \"Servidores MCP\",\n \"runtimeInherited\": \"Herdado em execução · novas sessões\",\n \"notSupported\": \"Não compatível com este provedor\",\n \"policyCompatibility\": \"Política MCP\",\n \"toolAllowlistSupported\": \"Lista de ferramentas compatível\",\n \"policyLimited\": \"Aplicam-se limites do provedor\",\n \"chatModelHint\": \"Configurações por agente · aplicadas no próximo turno\",\n \"appliesNextTurn\": \"Aplicado no próximo turno\",\n \"approvalMode\": \"Permissões de execução\",\n \"approvalPresets\": {\n \"ask\": {\n \"name\": \"Perguntar antes de alterar\",\n \"description\": \"Permite alterações no workspace e solicita confirmação antes de ações elevadas.\"\n },\n \"read-only\": {\n \"name\": \"Somente leitura\",\n \"description\": \"Inspeciona arquivos sem alterá-los.\"\n },\n \"workspace\": {\n \"name\": \"Acesso ao workspace\",\n \"description\": \"Permite alterações no workspace sem confirmação.\"\n },\n \"accept-edits\": {\n \"name\": \"Aceitar edições\",\n \"description\": \"Permite edições de arquivos mantendo as demais proteções.\"\n },\n \"plan\": {\n \"name\": \"Somente planejar\",\n \"description\": \"Analisa e planeja sem alterar arquivos.\"\n },\n \"auto\": {\n \"name\": \"Automático\",\n \"description\": \"Deixa o Qoder decidir quais ações seguras podem ser executadas.\"\n },\n \"full-access\": {\n \"name\": \"Acesso total\",\n \"description\": \"Permite execução local irrestrita sem confirmação.\"\n }\n }\n },\n \"create\": \"Criar Agente\",\n \"createTitle\": \"Criar Novo Agente\",\n \"createSuccess\": \"Agente Criado successfully\",\n \"createFailed\": \"Falhou to Criar Agente\",\n \"edit\": \"Editar\",\n \"editTitle\": \"Editar Agente - {{name}}\",\n \"updateSuccess\": \"Agente Atualizado successfully\",\n \"updateFailed\": \"Falhou to Atualizar Agente\",\n \"delete\": \"Excluir\",\n \"deleteConfirm\": \"Confirmar Excluir Agente\",\n \"deleteConfirmDesc\": \"Agent will be unavailable after deletion, but workspace files will be kept\",\n \"deleteSuccess\": \"Agente deleted successfully\",\n \"deleteFailed\": \"Falhou to Excluir Agente\",\n \"selectAgent\": \"Selecionar Agente\",\n \"defaultDisplayName\": \"Agente Padrao\",\n \"currentWorkspace\": \"Agente Atual\",\n \"switchSuccess\": \"Agente switched successfully\",\n \"switchFailed\": \"Falhou to switch Agente\",\n \"loadFailed\": \"Falha ao load Agente list\",\n \"loadConfigFailed\": \"Falha ao load Agente Configuracao\",\n \"saveFailed\": \"Falhou to Salvar Agente\",\n \"idRequired\": \"Please enter Agente ID\",\n \"idLabel\": \"Agente ID (optional)\",\n \"idHelp\": \"Leave empty to auto-generate. Only letters, digits, hyphens and underscores allowed.\",\n \"idPattern\": \"ID can only contain letters, numbers, underscores and hyphens\",\n \"idPlaceholder\": \"e.g.: my-agent\",\n \"nameRequired\": \"Please enter Agente Nome\",\n \"namePlaceholder\": \"e.g.: My Agente\",\n \"descriptionPlaceholder\": \"Briefly describe this agent's purpose...\",\n \"workspaceHelp\": \"Leave empty to auto-generate in ~/.qwenpaw/workspaces/<id>\",\n \"dragHandleTooltip\": \"Drag to reorder Agentes\",\n \"reorderSuccess\": \"Agente order saved\",\n \"reorderFailed\": \"Falha ao Salvar Agente order\",\n \"defaultNotEditable\": \"Default agent cannot be edited\",\n \"defaultNotDeletable\": \"Default agent cannot be deleted\",\n \"disable\": \"Desativar\",\n \"enable\": \"Ativar\",\n \"disabled\": \"Desativado\",\n \"disableConfirm\": \"Confirmar Desativar Agente\",\n \"disableConfirmDesc\": \"The agent instance will not start after disabling, but will remain visible in the list\",\n \"enableConfirm\": \"Confirmar Ativar Agente\",\n \"enableConfirmDesc\": \"The agent will be available for switching after enabling\",\n \"disableSuccess\": \"Agente Desativado successfully\",\n \"enableSuccess\": \"Agente Ativado successfully\",\n \"toggleFailed\": \"Falha ao toggle Agente state\",\n \"enableAgent\": \"Ativar {{name}}\",\n \"disableAgent\": \"Desativar {{name}}\",\n \"disabledAgents\": \"Desativados ({{count}})\",\n \"pinnedAgents\": \"Fixados\",\n \"otherAgents\": \"Outros agentes\",\n \"pinned\": \"Fixado\",\n \"defaultPinned\": \"O agente padrão está sempre fixado\",\n \"longPressToPin\": \"Pressione e segure para fixar\",\n \"longPressToUnpin\": \"Pressione e segure para desafixar\",\n \"pinAgent\": \"Fixar agente\",\n \"unpinAgent\": \"Desafixar agente\",\n \"pinSuccess\": \"Agente fixado\",\n \"unpinSuccess\": \"Agente desafixado\",\n \"pinFailed\": \"Falha ao atualizar o estado de fixação\",\n \"status\": {\n \"disabled\": \"Desativado\",\n \"pending\": \"Aguardando inicialização\",\n \"starting\": \"Inicializando\",\n \"running\": \"Em execução\",\n \"failed\": \"Falha na inicialização\",\n \"waitUntilStarted\": \"Disponível após concluir a inicialização\"\n },\n \"defaultNotDisablable\": \"Default agent cannot be disabled\",\n \"currentAgentDeleted\": \"Current agent has been deleted, automatically switched to default agent\",\n \"currentAgentDisabled\": \"Current agent has been disabled, automatically switched to default agent\",\n \"switchedToDefault\": \"Automatically switched to default agent\",\n \"cannotSwitchToDisabled\": \"This agent is disabled, please enable it in the management page first\",\n \"initialSkills\": \"Initial Skills\",\n \"initialSkillsHelp\": \"Select skills to add from the pool. You can add or remove skills later.\",\n \"selectAll\": \"Todos\",\n \"selectBuiltin\": \"Builtin\",\n \"selectNone\": \"Nenhum\",\n \"noPoolSkills\": \"No skills available in pool\",\n \"addSkillsToAgent\": \"Adicionar Skills\",\n \"model\": \"Modelo\",\n \"modelHelp\": \"Assign a specific LLM model to this agent. Leave empty to use the global default.\",\n \"modelPlaceholder\": \"Use global Padrao\",\n \"noConfiguredModels\": \"Nao configured Modelos\",\n \"modelColumn\": \"Modelo\",\n \"copyTooltip\": \"Copiar configuracao do agente\",\n \"copyDefaultTooltip\": \"Copiar usando o agente padrao como modelo\",\n \"copyTitle\": \"Copiar Configuracao do Agente\",\n \"copyNameLabel\": \"Nome do novo agente\",\n \"copyOptionsLabel\": \"Conteudo a copiar\",\n \"copyOptionAgentJson\": \"agent.json (obrigatorio)\",\n \"copyOptionAgentJsonHint\": \"Sempre copia a configuracao de origem analisada e redefine ID, nome, workspace e channels. Nao e uma copia byte a byte do agent.json no disco.\",\n \"copyOptionMdFiles\": \"AGENTS / SOUL / PROFILE / HEARTBEAT / BOOTSTRAP.md\",\n \"copyOptionSkills\": \"skills/ + manifest\",\n \"copyOptionJobs\": \"jobs.json\",\n \"copyOptionJobsHint\": \"Copia apenas as definicoes de tarefas, sem historico. O novo agente as executara conforme o agendamento logo apos a criacao.\",\n \"copySuccess\": \"Configuracao do agente copiada com sucesso\",\n \"copyFailed\": \"Falha ao copiar a configuracao do agente\"\n },\n \"skills\": {\n \"title\": \"Skills\",\n \"description\": \"Manage agent skills and capabilities.\",\n \"qwenpawManaged\": \"Gerenciado pelo QwenPaw\",\n \"qwenpawManagedHint\": \"Editável e projetado em agentes de terceiros compatíveis durante a execução.\",\n \"providerManaged\": \"Skills do agente de terceiros\",\n \"providerManagedHint\": \"Descobertas no agente de terceiros atual e exibidas somente para leitura.\",\n \"providerOnly\": \"Somente o agente de terceiros atual\",\n \"readOnly\": \"Somente leitura\",\n \"noDescription\": \"Nenhuma descrição disponível.\",\n \"providerSkillDetails\": \"Detalhes da Skill do agente de terceiros\",\n \"providerManagedDetailHint\": \"Esta Skill é gerenciada pelo agente de terceiros. O QwenPaw pode exibi-la e usá-la, mas não pode modificá-la.\",\n \"provider\": \"Agente de terceiros\",\n \"scope\": \"Escopo\",\n \"statusLabel\": \"Status\",\n \"descriptionLabel\": \"Descrição\",\n \"importHub\": \"Enviar via URL\",\n \"supportedSources\": \"Fontes suportadas:\",\n \"importSkills\": \"Importar Skill\",\n \"enterSkillUrl\": \"Enter Skill URL\",\n \"supportedSkillUrlSources\": \"Supported Skill Marketplaces:\",\n \"urlExamples\": \"URL examples:\",\n \"urlValid\": \"Valid URL from {{source}}\",\n \"invalidUrl\": \"Invalid URL\",\n \"clickToFill\": \"Click to fill in URL\",\n \"invalidSkillUrlSource\": \"Please verify the skill URL is from a supported marketplace\",\n \"type\": \"Tipo\",\n \"installedFrom\": \"Instalado de\",\n \"source\": \"Origem\",\n \"path\": \"Caminho\",\n \"tags\": \"Tags\",\n \"builtin\": \"Integrado\",\n \"custom\": \"Personalizado\",\n \"addSkill\": \"Adicionar Skill\",\n \"createSkill\": \"Criar Skill\",\n \"viewSkill\": \"Visualizar Skill\",\n \"editSkill\": \"Editar Skill\",\n \"skillName\": \"Skill Nome\",\n \"skillContent\": \"Skill Conteudo\",\n \"pleaseInputName\": \"Please input Skill Nome\",\n \"pleaseInputContent\": \"Please input Skill Conteudo\",\n \"skillNamePlaceholder\": \"e.g., weather_query\",\n \"contentPlaceholder\": \"[Format]\\n---\\nname: skill_name (required, lowercase with underscores)\\ndescription: Brief description (required)\\n---\\n\\nSkill implementation (Markdown format)\\n\\n[Example]\\n---\\nname: weather_query\\ndescription: Query weather info for a city\\n---\\n\\n## Features\\nQuery real-time weather data.\\n\\n## Usage\\nUser inputs city name, returns weather info.\",\n \"createSuccess\": \"Skill Criado successfully\",\n \"createFailed\": \"Falhou to Criar Skill\",\n \"deleteConfirm\": \"Are you sure you want to delete this skill?\",\n \"deleteSuccess\": \"Skill deleted successfully\",\n \"deleteFailed\": \"Falhou to Excluir Skill\",\n \"batchEnable\": \"Ativar\",\n \"batchDisable\": \"Desativar\",\n \"batchEnableSuccess\": \"{{count}} skills ativadas\",\n \"batchEnablePartial\": \"{{enabled}} ativadas, {{failed}} falharam\",\n \"batchEnableFailed\": \"Falha ao ativar em lote\",\n \"batchDisableSuccess\": \"{{count}} skills desativadas\",\n \"batchDisablePartial\": \"{{disabled}} desativadas, {{failed}} falharam\",\n \"batchDisableFailed\": \"Falha ao desativar em lote\",\n \"batchDeleteTitle\": \"Excluir {{count}} skills?\",\n \"batchDeleteSuccess\": \"{{count}} Skills deleted\",\n \"batchDeletePartial\": \"{{deleted}} deleted, {{failed}} Falhou\",\n \"batchDeleteFailed\": \"Batch Excluir Falhou\",\n \"selectedCount\": \"{{count}} Selecionado\",\n \"updateSuccess\": \"Skill Atualizado successfully\",\n \"updateFailed\": \"Falhou to Atualizar Skill\",\n \"frontmatterRequired\": \"Skills must start and end with ---\",\n \"frontmatterNameRequired\": \"Skills missing required field : name\",\n \"frontmatterDescriptionRequired\": \"Skills missing required field : description\",\n \"editNotSupported\": \"Falhou to Editar Skill\",\n \"editNote\": \"Tip: You can edit skill content, channels, and config here.\",\n \"create\": \"Criar\",\n \"optimizeWithAI\": \"AI Optimize\",\n \"stopOptimize\": \"Parar\",\n \"optimizeSuccess\": \"Skill optimized successfully\",\n \"optimizeFailed\": \"Falhou to optimize Skill\",\n \"noContentToOptimize\": \"Nao Conteudo to optimize\",\n \"cancelImport\": \"Cancelar Importar\",\n \"importCancelled\": \"Skill Importar cancelled\",\n \"importTimeout\": \"Skill import timed out. Please retry or check your network.\",\n \"uploadSkill\": \"Enviar Skill\",\n \"uploadZip\": \"Enviar via Zip\",\n \"uploadSuccess\": \"Skill uploaded successfully\",\n \"uploadFailed\": \"Skill Enviar Falhou\",\n \"uploadNoChange\": \"No new skills were imported. They may already exist.\",\n \"zipOnly\": \"Only .zip files are supported for upload\",\n \"fileSizeExceeded\": \"File size exceeds {{limit}}MB limit. Current file: {{size}}MB\",\n \"channels\": \"Canais\",\n \"allChannels\": \"Todos\",\n \"lastUpdated\": \"Atualizado\",\n \"searchPlaceholder\": \"Filtrar by Nome\",\n \"listView\": \"List Visualizar\",\n \"gridView\": \"Grid Visualizar\",\n \"refreshHint\": \"Atualizar Skills from disk\",\n \"poolSync\": \"Sync Status\",\n \"uploadedToPool\": \"Uploaded to Skill pool\",\n \"downloadedToWorkspace\": \"Downloaded to Atual Workspace\",\n \"builtinUpgradeTitle\": \"Upgrade Integrado Skill\",\n \"builtinUpgradeContent\": \"A newer version of built-in skill \\\"{{name}}\\\" is available in the pool. Overwrite the current version?\",\n \"languageSwitchTitle\": \"Switch Skill Idioma\",\n \"languageSwitchContent\": \"The following skills have a different language version in the pool and will be overwritten:\",\n \"overwriteExistingList\": \"The following skills already exist and will be overwritten:\",\n \"nameConflict\": \"Name conflict detected. Suggested rename has been filled.\",\n \"uploadToPoolHint\": \"Upload selected workspace skills to the shared local skill pool\",\n \"downloadFromPool\": \"Carregar do Skill Pool\",\n \"uploadToPool\": \"Sync to Skill Pool\",\n \"selectWorkspaceSkill\": \"Selecionar Atual Workspace Skill\",\n \"selectPoolItem\": \"Selecionar Skill pool item\",\n \"renameOptional\": \"Rename (optional)\",\n \"renamePlaceholder\": \"Suggested rename will appear here on conflict\",\n \"renameSingleOnly\": \"Rename is available only when exactly one skill is selected.\",\n \"selectAll\": \"Selecionar Todos\",\n \"clearSelection\": \"Limpar\",\n \"downloadSkillPool\": \"Baixar Skill Pool\",\n \"uploadSkillPool\": \"Enviar Skill Pool\",\n \"config\": \"Configuracao\",\n \"configPlaceholder\": \"{\\\"KEY\\\": \\\"value\\\"}\",\n \"configInvalidJson\": \"Invalid JSON Formato\",\n \"configSaved\": \"Configuracao saved\",\n \"emptyStateBadge\": \"Novo workspaces Iniciar empty\",\n \"emptyStateTitle\": \"This agent does not have any skills yet\",\n \"emptyStateText\": \"This is expected. New workspaces do not come with skills automatically. Download skills from the local pool or create one here to equip this agent.\",\n \"emptyStateDownload\": \"Baixar from Skill Pool\",\n \"emptyStateCreate\": \"Criar First Skill\",\n \"loadFailed\": \"Falhou to load Skills\",\n \"refreshFailed\": \"Falhou to Atualizar Skills\",\n \"createdSuccessfully\": \"Criado successfully\",\n \"saveFailed\": \"Falhou to Salvar\",\n \"importFailed\": \"Importar Falhou\",\n \"disabledSuccessfully\": \"Desativado successfully\",\n \"enabledSuccessfully\": \"Ativado successfully\",\n \"operationFailed\": \"Operation Falhou\",\n \"provideUrl\": \"Please provide a hub skill URL\",\n \"validUrl\": \"Please enter a valid URL starting with http:// or https://\",\n \"importedSkill\": \"Imported skill: {{name}}\",\n \"batchOperation\": \"Batch Operation\",\n \"exitBatch\": \"Exit Batch\",\n \"noSearchResults\": \"No skills found matching your search\",\n \"filterByTag\": \"Filtrar by tag\",\n \"noTags\": \"Nao Tags available\",\n \"enabledSkills\": \"Skills Ativadas\",\n \"disabledSkills\": \"Skills Desativadas\",\n \"active\": \"ativas\"\n },\n \"skillPool\": {\n \"description\": \"Shared local skill pool for built-in skills and workspace uploads. External hub imports such as ClawHub or LobeHub are separate.\",\n \"broadcast\": \"Broadcast to Agentes\",\n \"broadcastHint\": \"Broadcast skills to selected agents\",\n \"refreshHint\": \"Refresh skill pool from disk\",\n \"deleteExternalConfirm\": \"Exclui os arquivos da skill externa em {{path}} do disco.\",\n \"deleteExternalBatchWarning\": \"As skills externas selecionadas serão excluídas permanentemente do disco.\",\n \"broadcastTitle\": \"Broadcast {{name}}\",\n \"importBuiltin\": \"Atualizar Integrado Skills\",\n \"importBuiltinHint\": \"Update built-in skills to the latest version\",\n \"importBuiltinAlertHint\": \"{{count}} built-in changes need review\",\n \"importBuiltinSuccess\": \"Built-ins imported: {{names}}\",\n \"importBuiltinConflicts\": \"Skipped due to name conflicts: {{names}}\",\n \"importBuiltinNoChanges\": \"No built-in changes to import\",\n \"importBuiltinFailed\": \"Integrado Importar Falhou\",\n \"importBuiltinConflictTitle\": \"Builtin conflicts detected\",\n \"importBuiltinConflictContent\": \"The following built-ins differ from the current pool copy. Confirm to overwrite them from source.\",\n \"sourceVersion\": \"Origem Versao\",\n \"currentVersion\": \"Atual Versao\",\n \"builtinLanguage\": \"Idioma\",\n \"langDefault\": \"Padrao\",\n \"langDefaultTooltip\": \"Keep each skill in its current language\",\n \"langEn\": \"English\",\n \"langZh\": \"Chinese\",\n \"builtinLanguageChangeTitle\": \"Switch built-in language?\",\n \"builtinLanguageChangeContent\": \"Overwrite the pool copy of \\\"{{name}}\\\" with the {{language}} built-in version?\",\n \"builtinLanguageChangeSuccess\": \"\\\"{{name}}\\\" switched to {{language}}\",\n \"builtinLanguageChangeFailed\": \"Falha ao switch built-in Idioma\",\n \"importStatusMissing\": \"Missing in pool\",\n \"importStatusCurrent\": \"Already Atual\",\n \"importStatusOutdated\": \"Outdated\",\n \"importStatusLanguageSwitchTo\": \"Overwrite with the {{language}} version\",\n \"importStatusConflict\": \"Conflict\",\n \"builtinNoticeSummary\": \"{{count}} built-in changes detected\",\n \"builtinNoticeLineAdded\": \"Added: {{names}}\",\n \"builtinNoticeLineMissing\": \"Missing: {{names}}\",\n \"builtinNoticeLineUpdated\": \"Updated: {{names}}\",\n \"builtinNoticeLineRemoved\": \"Removed: {{names}}\",\n \"zipImport\": \"ZIP Importar\",\n \"create\": \"Criar\",\n \"edit\": \"Editar\",\n \"delete\": \"Excluir\",\n \"descriptionLabel\": \"Descricao\",\n \"tags\": \"Tags\",\n \"tagsPlaceholder\": \"Select or type to add tags\",\n \"tagTooLong\": \"Tag must be at most {{max}} characters\",\n \"filterByTag\": \"Filtrar by tag\",\n \"version\": \"Versao\",\n \"createTitle\": \"Criar Skill Pool Item\",\n \"editTitle\": \"Editar {{name}}\",\n \"skillNamePlaceholder\": \"Skill Nome\",\n \"contentPlaceholder\": \"SKILL.md Conteudo\",\n \"nameConflict\": \"Name conflict detected. Suggested rename applied.\",\n \"deletedFromPool\": \"Deleted from Skill pool\",\n \"deleteBuiltinConfirm\": \"This removes the built-in skill from the pool. You can import it again later from source.\",\n \"deleteConfirm\": \"This removes the skill from the shared local skill pool.\",\n \"deleteTitle\": \"Excluir {{name}}?\",\n \"batchDeleteTitle\": \"Excluir {{count}} pool skills?\",\n \"batchDeleteSuccess\": \"{{count}} pool Skills deleted\",\n \"batchDeletePartial\": \"{{deleted}} deleted, {{failed}} Falhou\",\n \"batchDeleteFailed\": \"Batch Excluir Falhou\",\n \"zipImportFailed\": \"ZIP Importar Falhou\",\n \"externalHubHint\": \"External hub import is separate from the local Skill Pool.\",\n \"imported\": \"Imported: {{names}}\",\n \"noNewImports\": \"Nao Novo Skills imported\",\n \"allBuiltin\": \"Todos Integrado\",\n \"selectWorkspaces\": \"Broadcast to workspace(s)\",\n \"allWorkspaces\": \"Todos Workspaces\",\n \"optionalRename\": \"Opcional rename\",\n \"back\": \"Voltar\",\n \"skillCount\": \"{{count}} Skills\",\n \"skillName\": \"Skill Nome\",\n \"status\": \"Status\",\n \"installedFrom\": \"Instalado de\",\n \"updateBuiltin\": \"Atualizar\",\n \"updateConfirmTitle\": \"Atualizar {{name}}\",\n \"updateConfirmContent\": \"Update to version {{version}}? This will replace the current pool copy with the latest built-in version.\",\n \"updateSuccess\": \"Atualizado successfully\",\n \"updateFailed\": \"Atualizar Falhou\",\n \"broadcastSuccess\": \"Broadcast Concluido\",\n \"broadcastFailed\": \"Broadcast Falhou\",\n \"overwriteConfirm\": \"Overwrite existing skills?\",\n \"overwriteTargetsContent\": \"The following existing targets will be overwritten:\",\n \"builtinOverwriteTargetsContent\": \"The following built-in targets have different versions and will be overwritten:\",\n \"renameAndRetry\": \"A skill with this name already exists. Use the suggested name?\",\n \"multiConflictTitle\": \"Multiple Conflicts Detected\",\n \"multiConflictDesc\": \"The following skills already exist. Please resolve conflicts individually and retry:\",\n \"singleConflictRename\": \"\\\"{{name}}\\\" already exists. Rename to \\\"{{suggested}}\\\" to proceed?\",\n \"statusUpToDate\": \"Up-to-date\",\n \"statusSynced\": \"Synced\",\n \"statusOutdated\": \"Outdated\",\n \"statusNotSynced\": \"Not synced\",\n \"statusConflict\": \"Conflict\",\n \"renameEntry\": \"Novo Nome for \\\"{{name}}\\\":\",\n \"builtin\": \"Integrado\",\n \"custom\": \"Personalizado\"\n },\n \"mcp\": {\n \"title\": \"MCP Clients\",\n \"qwenpawManaged\": \"Gerenciado pelo QwenPaw\",\n \"qwenpawManagedHint\": \"A configuração compartilhada do painel é injetada nos Agents de terceiros compatíveis durante a execução.\",\n \"providerManaged\": \"MCP local do {{provider}}\",\n \"providerManagedHint\": \"Detectado na configuração do agente de terceiros; é somente leitura e exclusivo desse agente.\",\n \"providerOnly\": \"Somente o agente de terceiros\",\n \"readOnly\": \"Somente leitura\",\n \"create\": \"Criar Client\",\n \"formatSupport\": \"Supported formats\",\n \"standardFormat\": \"Padrao (mcpServers wrapper)\",\n \"directFormat\": \"Direct (key → config object)\",\n \"singleFormat\": \"Single client (flat fields)\",\n \"emptyState\": \"No MCP clients configured yet\",\n \"loadError\": \"Falha ao load MCP clients\",\n \"createSuccess\": \"MCP client Criado successfully\",\n \"createError\": \"Falha ao Criar MCP client\",\n \"updateSuccess\": \"MCP client Atualizado successfully\",\n \"updateError\": \"Falha ao Atualizar MCP client\",\n \"enableSuccess\": \"MCP client Ativado successfully\",\n \"disableSuccess\": \"MCP client Desativado successfully\",\n \"toggleError\": \"Falha ao toggle MCP client Status\",\n \"deleteConfirm\": \"Are you sure you want to delete this MCP client?\",\n \"deleteSuccess\": \"MCP client deleted successfully\",\n \"deleteError\": \"Falha ao Excluir MCP client\",\n \"maskedFieldHint\": \"Sensitive fields are masked with ***. In-place edits on masked values are not supported — replace with the full new value to modify.\",\n \"tools\": \"Ferramentas e Permissões\",\n \"noTools\": \"No tools available from this MCP server\",\n \"toolsLoadError\": \"Failed to load tools from MCP server\",\n \"toolsConnecting\": \"MCP server is still connecting, please try again in a moment\",\n \"toolSchema\": \"Input Schema\",\n \"access\": {\n \"title\": \"Acesso\",\n \"button\": \"Acesso\",\n \"summary\": \"Política de acesso MCP\",\n \"clientSection\": \"Acesso geral\",\n \"toolSection\": \"Acesso à ferramenta\",\n \"default\": \"Padrão\",\n \"sourceType\": \"Origem\",\n \"sourceValue\": \"Valor da origem\",\n \"sourceValueAllChannels\": \"Todos os canais\",\n \"subjectType\": \"Objeto\",\n \"subjectValue\": \"Valor do objeto\",\n \"effectLabel\": \"Política\",\n \"source\": {\n \"channel\": \"Canal\"\n },\n \"subjectTypeOption\": {\n \"all\": \"Todos\",\n \"user\": \"Usuário\"\n },\n \"sourceValuePlaceholder\": {\n \"channel\": \"console / dingtalk\"\n },\n \"subjectValuePlaceholder\": \"ex.: alice\",\n \"subjectValuePlaceholderSelectedSource\": \"ID de usuário na origem selecionada\",\n \"subjectValuePlaceholderScoped\": \"ID de usuário de {{source}}\",\n \"subjectValueAll\": \"Todos\",\n \"recentUserPlaceholder\": \"O ID de usuário é limitado à origem selecionada; digite ou selecione novamente\",\n \"noRecentUsers\": \"Nenhum usuário recente para esta origem; digite um ID manualmente\",\n \"ambiguousUserSourceWarning\": \"IDs de usuário são únicos apenas dentro da origem. Selecione um canal específico para evitar correspondência entre canais.\",\n \"unknownUserValueWarning\": \"Este ID de usuário não está nos usuários ativos recentes. Ainda é possível salvar.\",\n \"validation\": {\n \"missingUserValue\": \"Selecione uma origem específica e informe um ID de usuário válido.\",\n \"ambiguousUserSource\": \"Regras de usuário devem mirar um canal específico, não todos os canais.\",\n \"unknownUserValue\": \"Este ID de usuário não está nos usuários ativos recentes. Ainda é possível salvar.\"\n },\n \"addRule\": \"Adicionar regra\",\n \"deleteRule\": \"Excluir regra\",\n \"noClientRules\": \"Nenhuma regra\",\n \"noRules\": \"Nenhuma regra, usa a política padrão\",\n \"inherit\": \"Herdar ({{effect}})\",\n \"stale\": \"Não encontrado\",\n \"disabledTools\": \"Este cliente MCP está desativado; as regras de acesso salvas ainda podem ser editadas.\",\n \"discardTitle\": \"Descartar alterações não salvas?\",\n \"discardContent\": \"As alterações não salvas da política de acesso serão perdidas.\",\n \"loadError\": \"Falha ao carregar política de acesso MCP\",\n \"saveSuccess\": \"Política de acesso MCP salva\",\n \"saveError\": \"Falha ao salvar política de acesso MCP\",\n \"unmanagedRules\": \"{{count}} regra(s) avançada(s)\",\n \"effect\": {\n \"allow\": \"Permitir\",\n \"ask\": \"Perguntar\",\n \"deny\": \"Negar\"\n }\n },\n \"tab\": {\n \"json\": \"Importação JSON\",\n \"form\": \"Modo formulário\"\n },\n \"form\": {\n \"key\": \"Chave do cliente\",\n \"keyPlaceholder\": \"my-mcp-server\",\n \"keyRequired\": \"A chave do cliente é obrigatória\",\n \"name\": \"Nome de exibição\",\n \"namePlaceholder\": \"My MCP Server\",\n \"nameRequired\": \"O nome de exibição é obrigatório\",\n \"transport\": \"Transporte\",\n \"url\": \"URL do servidor\",\n \"urlRequired\": \"A URL é obrigatória para servidores MCP remotos\",\n \"command\": \"Comando\",\n \"commandRequired\": \"O comando é obrigatório para transporte stdio\",\n \"args\": \"Argumentos (separados por espaço ou nova linha)\",\n \"description\": \"Descrição\",\n \"descriptionPlaceholder\": \"Descrição opcional\",\n \"env\": \"Variáveis de ambiente\",\n \"envPlaceholder\": \"KEY=VALUE (uma por linha)\"\n },\n \"oauth\": {\n \"enableOAuth\": \"Usar autorização OAuth\",\n \"manage\": \"OAuth\",\n \"authorized\": \"Autorizado\",\n \"notAuthorized\": \"Não autorizado\",\n \"expired\": \"Token expirado\",\n \"failed\": \"Falha na autorização\",\n \"waiting\": \"Aguardando autorização...\",\n \"authorize\": \"Autorizar\",\n \"reauthorize\": \"Reautorizar\",\n \"revoke\": \"Revogar\",\n \"startFailed\": \"Falha ao iniciar fluxo OAuth\",\n \"authFailed\": \"Falha na autorização OAuth\",\n \"popupBlocked\": \"O popup foi bloqueado pelo navegador. Permita popups e tente novamente.\",\n \"windowClosed\": \"Janela de autorização fechada. Tente novamente.\",\n \"noUrl\": \"Insira a URL do servidor primeiro\",\n \"noClientKey\": \"Crie o cliente primeiro\",\n \"showAdvanced\": \"Opções avançadas\",\n \"hideAdvanced\": \"Ocultar opções avançadas\",\n \"clientId\": \"Client ID (opcional)\",\n \"clientIdPlaceholder\": \"Deixe em branco para usar registro dinâmico\",\n \"scope\": \"Scope (opcional)\",\n \"scopePlaceholder\": \"openid profile email\",\n \"authEndpoint\": \"Authorization Endpoint (substituir)\",\n \"tokenEndpoint\": \"Token Endpoint (substituir)\",\n \"endpointHint\": \"Deixe em branco para descoberta automática do servidor MCP.\"\n }\n },\n \"heartbeat\": {\n \"title\": \"Batimento\",\n \"description\": \"Execute HEARTBEAT.md em intervalo fixo para autoverificações. Por padrão roda em silêncio sem afetar conversas atuais; opcionalmente envie respostas ao último canal de chat.\",\n \"enabled\": \"Ativar heartbeat\",\n \"every\": \"Intervalo\",\n \"everyRequired\": \"Obrigatório\",\n \"everyMin\": \"Deve ser no mínimo 1\",\n \"timeoutSeconds\": \"Tempo limite de execução (segundos)\",\n \"timeoutRequired\": \"Obrigatório\",\n \"timeoutMin\": \"Deve ser no mínimo 1\",\n \"timeoutMax\": \"Deve ser no máximo 3600\",\n \"unitMinutes\": \"Minutos\",\n \"unitHours\": \"Horas\",\n \"target\": \"Destino da resposta\",\n \"targetMain\": \"Modo silencioso (padrão, sem saída no canal)\",\n \"targetLast\": \"Enviar para o último canal de chat\",\n \"activeHours\": \"Horário ativo (opcional)\",\n \"activeStart\": \"Hora de início\",\n \"activeEnd\": \"Hora de término\",\n \"loadFailed\": \"Falha ao carregar configuração do heartbeat\",\n \"saveSuccess\": \"Salvo com sucesso; heartbeat recarregado em tempo real\",\n \"saveFailed\": \"Falha ao salvar configuração do heartbeat\",\n \"targetInbox\": \"Enviar para o Inbox\",\n \"runNow\": \"Executar agora\",\n \"runNowSuccess\": \"Heartbeat disparado. Verifique o Inbox ou o canal de destino em instantes.\",\n \"runNowFailed\": \"Falha ao disparar execução do heartbeat\"\n },\n \"cronJobs\": {\n \"title\": \"Tarefas Cron\",\n \"description\": \"Crie e gerencie tarefas agendadas executadas automaticamente nos horários definidos.\",\n \"createJob\": \"Criar job\",\n \"editJob\": \"Editar job\",\n \"confirmDelete\": \"Confirmar exclusão\",\n \"deleteConfirm\": \"Tem certeza de que deseja excluir este Cron Job?\",\n \"okText\": \"OK\",\n \"cancelText\": \"Cancelar\",\n \"deleteText\": \"Excluir\",\n \"id\": \"ID do job\",\n \"name\": \"Nome do job\",\n \"enabled\": \"Estado\",\n \"scheduleType\": \"Tipo de agendamento\",\n \"scheduleCron\": \"Agendamento (Cron)\",\n \"scheduleCronLabel\": \"Agendamento (Cron)\",\n \"scheduleTimezone\": \"Fuso horário\",\n \"cronType\": \"Frequência\",\n \"cronTypeHourly\": \"A cada hora\",\n \"cronTypeDaily\": \"Diário\",\n \"cronTypeWeekly\": \"Semanal\",\n \"cronTypeCustom\": \"Personalizado\",\n \"cronTime\": \"Horário\",\n \"cronDaysOfWeek\": \"Dias da semana\",\n \"cronDayMon\": \"Segunda-feira\",\n \"cronDayTue\": \"Terça-feira\",\n \"cronDayWed\": \"Quarta-feira\",\n \"cronDayThu\": \"Quinta-feira\",\n \"cronDayFri\": \"Sexta-feira\",\n \"cronDaySat\": \"Sábado\",\n \"cronDaySun\": \"Domingo\",\n \"cronCustomExpression\": \"Expressão Cron\",\n \"taskText\": \"Conteúdo da mensagem\",\n \"action\": \"Ação\",\n \"disable\": \"Desativar\",\n \"executeNow\": \"Executar agora\",\n \"edit\": \"Editar\",\n \"delete\": \"Excluir\",\n \"totalItems\": \"Total de {{count}} itens\",\n \"perPage\": \"/ página\",\n \"pleaseInputId\": \"Informe o ID do job\",\n \"pleaseInputName\": \"Informe o nome do job\",\n \"pleaseInputCron\": \"Informe a expressão cron\",\n \"pleaseSelectTaskType\": \"Selecione o tipo de tarefa\",\n \"pleaseInputRequest\": \"Informe o conteúdo da requisição\",\n \"pleaseInputMessageContent\": \"Informe o conteúdo da mensagem\",\n \"pleaseInputChannel\": \"Informe o canal de destino\",\n \"pleaseInputUserId\": \"Informe o ID do usuário de destino\",\n \"pleaseInputSessionId\": \"Informe o ID da sessão de destino\",\n \"jobIdPlaceholder\": \"ex.: daily-report-job\",\n \"jobNamePlaceholder\": \"ex.: Relatório matinal diário\",\n \"selectTimezone\": \"Selecionar fuso horário\",\n \"taskDescriptionPlaceholder\": \"Para tarefas de texto simples, digite o corpo da mensagem a enviar...\",\n \"invalidJsonFormat\": \"Formato JSON inválido\",\n \"jsonFormatRequired\": \"Formato JSON obrigatório\",\n \"taskType\": \"Tipo de tarefa\",\n \"text\": \"Conteúdo da mensagem\",\n \"requestInput\": \"Conteúdo da requisição\",\n \"requestSessionId\": \"ID da sessão da requisição\",\n \"requestUserId\": \"ID do usuário da requisição\",\n \"dispatchChannel\": \"Canal de destino\",\n \"dispatchTargetUserId\": \"ID do usuário de destino\",\n \"dispatchTargetSessionId\": \"ID da sessão de destino\",\n \"dispatchMode\": \"Modo de entrega\",\n \"silentDelivery\": \"Entrega silenciosa\",\n \"runtimeMaxConcurrency\": \"Concorrência máxima\",\n \"runtimeTimeoutSeconds\": \"Tempo limite (segundos)\",\n \"runtimeMisfireGraceSeconds\": \"Tolerância de execução perdida (segundos)\",\n \"runtimeToolSafety\": \"Verificação de segurança de ferramentas\",\n \"idTooltip\": \"Gerado pelo sistema, não editável: UUID único atribuído na criação da tarefa.\",\n \"nameTooltip\": \"Um nome amigável para identificar este job.\",\n \"cronTooltip\": \"Define quando a tarefa deve ser executada\",\n \"cronExample\": \"Exemplos comuns: '0 9 * * *' = 9h diariamente | '*/30 * * * *' = a cada 30 min | '0 */2 * * *' = a cada 2 horas | '0 0 * * 0' = meia-noite de domingo\",\n \"cronHelper\": \"Novo em expressões Cron?\",\n \"cronHelperLink\": \"Usar gerador online\",\n \"timezoneTooltip\": \"Fuso horário do agendamento cron. Padrão: UTC\",\n \"taskTypeTooltip\": \"Escolha 'text' para mensagens simples ou 'agent' para fluxos complexos com agente.\",\n \"textTooltip\": \"Tarefa de mensagem simples: este é o corpo da mensagem. Obrigatório quando o tipo é 'text'.\",\n \"requestInputTooltip\": \"Conteúdo da mensagem em JSON. É o que o agente receberá e processará. Obrigatório quando o tipo é 'agent'.\",\n \"requestInputExample\": \"Formato: [{\\\"role\\\":\\\"user\\\",\\\"content\\\":[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Sua mensagem aqui\\\"}]}]\",\n \"requestSessionIdTooltip\": \"ID da sessão para o contexto da requisição. Use 'default' se não tiver certeza.\",\n \"requestUserIdTooltip\": \"ID do usuário que inicia a requisição. Use 'system' para tarefas automatizadas.\",\n \"dispatchChannelTooltip\": \"Canal de destino onde a resposta será enviada (ex.: 'console', 'discord', 'imessage').\",\n \"dispatchTargetUserIdTooltip\": \"ID do usuário que receberá a resposta no canal de destino.\",\n \"dispatchTargetSessionIdTooltip\": \"ID da sessão em que a resposta será entregue no canal de destino.\",\n \"dispatchModeTooltip\": \"Escolha 'stream' para respostas em tempo real ou 'final' apenas para respostas completas.\",\n \"silentDeliveryTooltip\": \"Executa a tarefa do agente e mantém a sessão e o rastreamento, mas não envia o resultado ao canal.\",\n \"maxConcurrencyTooltip\": \"Número máximo de execuções simultâneas deste job. Padrão: 1\",\n \"timeoutSecondsTooltip\": \"Tempo máximo de execução em segundos. O job será encerrado se ultrapassar.\",\n \"misfireGraceSecondsTooltip\": \"Período de tolerância para execuções perdidas. Se o job atrasar mais que isso em relação ao horário agendado, não será executado.\",\n \"toolSafetyTooltip\": \"Quando ativado, chamadas de ferramentas de risco requerem aprovação do usuário (pode bloquear jobs cron sem supervisão). Quando desativado, todas as ferramentas são executadas sem aprovação. Padrão: desativado\",\n \"executeNowTitle\": \"Executar tarefa agora\",\n \"executeNowContent\": \"Deseja executar \\\"{{name}}\\\" agora?\",\n \"executeNowConfirm\": \"Executar agora\",\n \"createFromTemplate\": \"Criar a partir de modelo\",\n \"templateModalTitle\": \"Escolha um modelo de tarefa\",\n \"templateModalDescription\": \"Escolha um modelo integrado e ajuste os principais parâmetros no painel antes de salvar.\",\n \"useTemplate\": \"Usar modelo\",\n \"templateTagPersonal\": \"Pessoal\",\n \"templateTagTeam\": \"Equipe\",\n \"templateTagReminder\": \"Lembrete\",\n \"templateTagCalendar\": \"Calendário\",\n \"templateTagCalendarRecommended\": \"Recomendado no calendário\",\n \"saveResultToInbox\": \"Salvar resultado no Inbox\",\n \"saveResultToInboxTooltip\": \"Quando ativado, execuções com entrega bem-sucedida ficam no Inbox. Falhas de entrega sempre são gravadas no Inbox como contingência.\",\n \"scheduleFilterAll\": \"Todos os tipos de agendamento\",\n \"scheduleTypeRecurring\": \"Recorrente\",\n \"scheduleTypeOnce\": \"Agendada\",\n \"onceRunAt\": \"Executar em\",\n \"repeatEnabled\": \"Repetir\",\n \"repeatEnabledTooltip\": \"Ativa agendamento recorrente a partir deste horário inicial\",\n \"repeatFrequency\": \"Frequência de repetição\",\n \"repeatEveryDays\": \"Repetir a cada (dias)\",\n \"repeatEveryPrefix\": \"A cada\",\n \"repeatEverySuffix\": \"dia(s)\",\n \"repeatEndType\": \"Fim da repetição\",\n \"repeatEndNever\": \"Nunca\",\n \"repeatEndUntil\": \"Encerrar em data\",\n \"repeatEndCount\": \"Contagem limitada\",\n \"repeatUntil\": \"Data final\",\n \"repeatCount\": \"Número de execuções\",\n \"pleaseSelectScheduleType\": \"Selecione o tipo de agendamento\",\n \"pleaseInputRunAt\": \"Selecione o horário de execução\",\n \"pleaseInputRepeatEveryDays\": \"Informe o intervalo de repetição (dias)\",\n \"pleaseSelectRepeatEndType\": \"Selecione a condição de término\",\n \"pleaseInputRepeatUntil\": \"Selecione a data final\",\n \"pleaseInputRepeatCount\": \"Informe o número de execuções\",\n \"validation\": {\n \"cronRequired\": \"A expressão cron é obrigatória para agendamentos recorrentes.\",\n \"runAtRequired\": \"O horário de execução é obrigatório para agendamentos únicos.\",\n \"repeatUntilRequired\": \"Ao usar \\\"Encerrar em data\\\", selecione a data final.\",\n \"repeatCountRequired\": \"Ao usar \\\"Contagem limitada\\\", informe o número de execuções.\",\n \"repeatUntilAfterRunAt\": \"A data final deve ser posterior ao horário de execução. Escolha uma data final após o horário de execução.\",\n \"textRequired\": \"O conteúdo da mensagem é obrigatório quando o tipo da tarefa é text.\",\n \"requestRequired\": \"O conteúdo da requisição é obrigatório quando o tipo da tarefa é agent.\",\n \"invalidCronExpression\": \"Expressão cron inválida. Use o formato cron de 5 campos.\"\n },\n \"runtimeShareSession\": \"Compartilhar sessão\",\n \"shareSessionTooltip\": \"Quando ativado, compartilha a sessão com o usuário de destino. Quando desativado, cada execução cria contexto isolado com ID de execução único. Adequado para tarefas independentes sem memória. Padrão: ativado\",\n \"listView\": \"Lista\",\n \"calendarView\": \"Calendário\",\n \"calendarEmptyHint\": \"Ainda não há jobs agendados. Crie jobs agendados para vê-los no calendário.\",\n \"calendarMoreItems\": \"+{{count}} a mais\",\n \"calendarDayListTitle\": \"Jobs agendados em {{date}}\",\n \"executionHistory\": \"Histórico\",\n \"historyTitle\": \"Histórico — {{name}}\",\n \"historyEmpty\": \"Ainda não há registros de execução.\",\n \"historyStatusSuccess\": \"Sucesso\",\n \"historyStatusFailed\": \"Falhou\",\n \"historyStatusRunning\": \"Em execução\",\n \"historyStatusCancelled\": \"Cancelado\",\n \"historyTriggerScheduled\": \"Disparado pelo agendamento\",\n \"historyTriggerManual\": \"Disparado manualmente\",\n \"historyExpand\": \"Expandir\",\n \"historyCollapse\": \"Recolher\",\n \"templates\": {\n \"dailyTechNewsBrief\": {\n \"title\": \"Resumo diário de notícias de tecnologia\",\n \"description\": \"Às 09:30 em dias úteis, compila manchetes em alta em um breve resumo.\",\n \"frequency\": \"Dias úteis 09:30 (30 9 * * 1-5)\"\n },\n \"dailyEveningReflection\": {\n \"title\": \"Reflexão vespertina diária\",\n \"description\": \"Às 20:00 em dias úteis, sugere uma reflexão rápida sobre o trabalho do dia.\",\n \"frequency\": \"Dias úteis 20:00 (0 20 * * 1-5)\"\n },\n \"weekendRelaxationReminder\": {\n \"title\": \"Lembrete de descanso no fim de semana\",\n \"description\": \"Nas manhãs de fim de semana, envia sugestões de filmes em alta para um descanso mais leve.\",\n \"frequency\": \"Fins de semana 10:00 (0 10 * * 6,0)\"\n },\n \"pomodoroBreakReminder\": {\n \"title\": \"Lembrete de pausa Pomodoro\",\n \"description\": \"No horário comercial, lembre a cada 25 minutos de se hidratar ou descansar os olhos.\",\n \"frequency\": \"Dias úteis 09:00–17:59 a cada 25 min (*/25 9-17 * * 1-5)\"\n },\n \"petCareReminder\": {\n \"title\": \"Lembrete de vermifugação/vacina do pet\",\n \"description\": \"No dia 15 de cada mês à noite, lembre sobre vermifugação ou retorno de vacina do pet.\",\n \"frequency\": \"Mensalmente no dia 15 às 20:00 (0 20 15 * *)\"\n },\n \"onceTextBirthdayReminder\": {\n \"title\": \"Lembrete de aniversário\",\n \"description\": \"Lembrar que xx faz aniversário em 1º de janeiro.\",\n \"frequency\": \"Execução: 2026-01-01 09:00\"\n },\n \"onceAgentBusinessTripPrep\": {\n \"title\": \"Preparação para viagem de trabalho\",\n \"description\": \"Consultar o clima do destino e sugerir preparativos para a viagem.\",\n \"frequency\": \"Execução: 2026-01-01 20:00\"\n },\n \"repeatCountTextMedicineReminder\": {\n \"title\": \"Lembrete de medicação (14 vezes)\",\n \"description\": \"Lembrar de tomar o remédio no horário por um ciclo de 14 execuções.\",\n \"frequency\": \"Primeira execução: 2026-01-01 09:00 | A cada 1 dia | 14 execuções\"\n },\n \"repeatCountAgentDietPlan\": {\n \"title\": \"Plano alimentar de 14 dias\",\n \"description\": \"Gerar e enviar sugestões diárias de alimentação por 14 dias.\",\n \"frequency\": \"Primeira execução: 2026-01-01 08:00 | A cada 1 dia | 14 execuções\"\n },\n \"repeatUntilTextWeeklyMeeting\": {\n \"title\": \"Lembrete de reunião semanal (2 meses)\",\n \"description\": \"Enviar lembrete semanal da reunião até a data limite configurada.\",\n \"frequency\": \"Primeira execução: 2026-01-02 08:45 | A cada 7 dias | Até 2026-03-01\"\n },\n \"repeatUntilAgentWeeklySummary\": {\n \"title\": \"Resumo semanal antes da reunião\",\n \"description\": \"Antes de cada reunião semanal, gerar um resumo de trabalho com base na memory da última semana.\",\n \"frequency\": \"Primeira execução: 2026-01-02 08:30 | A cada 7 dias | Até 2026-03-01\"\n }\n }\n },\n \"channels\": {\n \"title\": \"Canais\",\n \"description\": \"Manage and configure message channels\",\n \"loading\": \"Carregando channels...\",\n \"configSaved\": \"Configuracao saved successfully\",\n \"configFailed\": \"Falhou to Salvar Configuracao\",\n \"channelType\": \"Channel Tipo\",\n \"status\": \"Status\",\n \"totalItems\": \"Total {{count}} items\",\n \"botPrefix\": \"Bot Prefix\",\n \"showToolCalls\": \"Mostrar Informações de Chamada de Ferramenta\",\n \"showToolCallsTooltip\": \"Mostrar informações de chamada de ferramenta aos usuários (desative para ocultar)\",\n \"showToolResults\": \"Mostrar Informações do Resultado da Ferramenta\",\n \"showToolResultsTooltip\": \"Mostrar informações do resultado da ferramenta aos usuários (desative para ocultar)\",\n \"toolCallMaxLength\": \"Comprimento Máximo de Exibição da Chamada de Ferramenta\",\n \"toolResultMaxLength\": \"Comprimento Máximo de Exibição do Resultado da Ferramenta\",\n \"toolMaxLengthTooltip\": \"Defina como 0 para não limitar o comprimento\",\n \"onebotMediaBase64\": \"Enviar mídia local como Base64\",\n \"onebotMediaBase64Tooltip\": \"Escolha como os arquivos de mídia são enviados. Quando desativado, o caminho do arquivo é enviado; quando ativado, o arquivo é codificado em Base64. URLs remotas permanecem inalteradas.\",\n \"onebotMediaBase64MaxMb\": \"Limite de tamanho do arquivo Base64\",\n \"onebotMediaBase64MaxMbTooltip\": \"Arquivos de mídia acima deste limite são enviados usando o caminho original do arquivo.\",\n \"onebotMediaBase64MaxMbRequired\": \"Informe o limite de tamanho do arquivo Base64\",\n \"onebotMediaBase64MaxMbMin\": \"O limite de tamanho do arquivo Base64 deve ser de pelo menos 1 MB\",\n \"showThinking\": \"Mostrar Thinking\",\n \"showThinkingTooltip\": \"Show model thinking/reasoning content to users (turn off to hide)\",\n \"noTextDebounce\": \"Debounce sem texto\",\n \"noTextDebounceTooltip\": \"Quando ativado, mensagens apenas com mídia (imagem/vídeo/arquivo) são armazenadas em buffer até que uma mensagem de texto chegue, depois mescladas. Desative para processar todas as mensagens imediatamente. Mensagens de voz são sempre processadas imediatamente.\",\n \"streamingEnabled\": \"Saída em Streaming\",\n \"streamingEnabledDingtalkHint\": \"Apenas efetivo quando o tipo de mensagem é Card\",\n \"streamingEnabledFeishuHint\": \"Requer habilitar a permissão cardkit:card:write na página de gerenciamento de permissões da Plataforma Aberta Feishu\",\n \"ackMessage\": \"Acknowledgment Message\",\n \"ackMessageTooltip\": \"Send an instant reply when a message is received, before agent processing begins. Leave empty to disable.\",\n \"ackMessagePlaceholder\": \"⏳ Processing...\",\n \"notSet\": \"Nao definido\",\n \"clickCardToEdit\": \"Click card to Editar\",\n \"settings\": \"Configuracoes\",\n \"channelSettings\": \"Channel Configuracoes\",\n \"pleaseInputDbPath\": \"Please input DB Caminho\",\n \"pleaseInputPollInterval\": \"Please input poll interval\",\n \"discordBotToken\": \"Discord bot token\",\n \"acceptBotMessages\": \"Accept Bot Messages\",\n \"acceptBotMessagesTooltip\": \"When enabled, messages from other bots will be accepted\",\n \"httpProxyPlaceholder\": \"http://127.0.0.1:18118\",\n \"httpProxyAuthPlaceholder\": \"user:password\",\n \"dbPathPlaceholder\": \"~/Library/Messages/chat.db\",\n \"botPrefixPlaceholder\": \"@bot\",\n \"dmPolicy\": \"DM Policy\",\n \"dmPolicyTooltip\": \"Control who can interact with the bot via direct message. Open: everyone; Allowlist: authorized users only\",\n \"groupPolicy\": \"Group Policy\",\n \"groupPolicyTooltip\": \"Control who can interact with the bot in group chats. Open: everyone; Allowlist: authorized users only\",\n \"requireMention\": \"Require @Mention\",\n \"requireMentionTooltip\": \"When enabled, bot only responds in group chats when explicitly @mentioned\",\n \"policyOpen\": \"Abrir\",\n \"policyAllowlist\": \"Allowlist\",\n \"allowFrom\": \"Allowlist Users\",\n \"allowFromTooltip\": \"List of user IDs allowed to interact with the bot. Enable allowlist first, then DM the bot to see your ID in the deny message\",\n \"allowFromPlaceholder\": \"Enter user ID and press Enter to add\",\n \"denyMessage\": \"Access Denied Message\",\n \"denyMessageTooltip\": \"Custom message shown to users when access is denied\",\n \"twilioAccountSid\": \"Twilio Account SID\",\n \"twilioAuthToken\": \"Twilio Auth Token\",\n \"phoneNumber\": \"Phone Number\",\n \"phoneNumberSid\": \"Phone Number SID\",\n \"phoneNumberSidHelp\": \"Found under Phone Numbers → Active Numbers in the Twilio Console.\",\n \"ttsProvider\": \"TTS Provedor\",\n \"ttsVoice\": \"TTS Voice\",\n \"sttProvider\": \"STT Provedor\",\n \"language\": \"Idioma\",\n \"welcomeGreeting\": \"Welcome Greeting\",\n \"welcomeText\": \"Welcome Message\",\n \"welcomeTextTooltip\": \"Message automatically sent when a user enters a single chat session with the bot for the first time that day\",\n \"welcomeTextPlaceholder\": \"e.g. Hello! I'm QwenPaw. How can I help you?\",\n \"loginWeCom\": \"Get WeCom QR Code\",\n \"wecomSetupGuide\": \"When creating a bot via QR code, please select \\\"Skip Authorization\\\" when asked to authorize bot capabilities. If you select \\\"Confirm Authorization\\\", users other than the bot creator may not be able to interact with the bot.\",\n \"wecomScanAuth\": \"Scan to Authorize\",\n \"wecomAuthHint\": \"Scan the QR code above with WeCom to authorize. Bot ID and Secret will be filled in automatically.\",\n \"wecomAuthSuccess\": \"WeCom bot authorized. Bot ID and Secret have been filled in.\",\n \"wecomQrcodeFailed\": \"Failed to get WeCom QR code. Please try again.\",\n \"slackBotToken\": \"Bot Token\",\n \"slackBotTokenTooltip\": \"Slack Bot User OAuth Token, começa com xoxb-\",\n \"slackAppToken\": \"App Token\",\n \"slackAppTokenTooltip\": \"Slack App-Level Token para Socket Mode, começa com xapp-\",\n \"slackProxyTooltip\": \"URL de proxy HTTP para conectar à API do Slack\",\n \"dingtalkSetupGuide\": \"Scan the QR code with DingTalk to create a bot instantly. A DingTalk app will be created automatically and Client ID / Client Secret will be filled in.\",\n \"dingtalkScanAuth\": \"Scan to Criar Bot\",\n \"dingtalkGetQrcode\": \"Get DingTalk QR Code\",\n \"dingtalkScanHint\": \"Scan the QR code above with DingTalk. Client ID and Client Secret will be filled in automatically after authorization.\",\n \"dingtalkAuthSuccess\": \"DingTalk bot created. Client ID and Client Secret have been filled in.\",\n \"dingtalkQrcodeFailed\": \"Failed to get DingTalk QR code. Please try again.\",\n \"dingtalkQrcodeExpired\": \"QR code expired, please get a new one.\",\n \"qqSetupGuide\": \"Use o QQ para escanear o código QR e autorizar o bot. Após escanear o código, o APP ID e o Client Secret serão obtidos automaticamente e preenchidos.\",\n \"qqScanAuth\": \"Escanear o código QR para autorizar o bot\",\n \"qqGetQrcode\": \"Obter o código QR do QQ\",\n \"qqScanHint\": \"Use o QQ para escanear o código QR acima. Após concluir a autorização, o APP ID e o Client Secret serão preenchidos automaticamente.\",\n \"qqAuthSuccess\": \"A autorização do bot do QQ foi bem-sucedida. O APP ID e o Client Secret já foram preenchidos automaticamente.\",\n \"qqQrcodeFailed\": \"Falha ao obter o código QR do QQ. Tente novamente mais tarde\",\n \"qqQrcodeExpired\": \"O código QR expirou. Obtenha-o novamente\",\n \"atSenderOnReply\": \"@ Sender Ligado Reply\",\n \"atSenderOnReplyTooltip\": \"When enabled, the bot will @mention the sender in its first reply message in group chats. Note: In card mode, @mention only works for enterprise internal members.\",\n \"dingtalkEndpoint\": \"API Endpoint\",\n \"dingtalkEndpointTooltip\": \"Endpoint de API personalizado para implantações privadas do DingTalk. Deixe em branco para usar o padrão oficial.\",\n \"wecomCancelled\": \"Autorizacao cancelled\",\n \"wecomAuthFailed\": \"Autorizacao failed: {{msg}}\",\n \"voiceSetupGuide\": \"Set up a Twilio account, purchase a phone number, then enter your credentials below. Find your Account SID and Auth Token on the Twilio Console dashboard. The Phone Number SID is listed under Phone Numbers → Active Numbers.\",\n \"voiceSetupLink\": \"Abrir Twilio Console\",\n \"sipSetupGuide\": \"Configure a SIP registrar (e.g. Asterisk, FreeSWITCH) and enter your SIP credentials below. In Dev mode, pyVoIP handles SIP/RTP locally. In Production mode, LiveKit SIP Server is used.\",\n \"sipMode\": \"SIP Modo\",\n \"sipModeTooltip\": \"Dev: pure-Python pyVoIP for local testing. Production: LiveKit SIP Server for high concurrency.\",\n \"sipServer\": \"SIP Server\",\n \"sipServerPlaceholder\": \"Leave empty to use built-in registrar\",\n \"sipServerPlaceholderLivekit\": \"Not needed for LiveKit mode\",\n \"sipUsername\": \"SIP Username\",\n \"sipPassword\": \"SIP Password\",\n \"sipPort\": \"SIP Port\",\n \"sipTransport\": \"Transport\",\n \"sipDashscopeApiKey\": \"DashScope API Chave\",\n \"sipDashscopeApiKeyTooltip\": \"API key for Aliyun DashScope STT/TTS. If empty, falls back to the DASHSCOPE_API_KEY environment variable.\",\n \"livekitUrl\": \"LiveKit URL\",\n \"livekitApiKey\": \"LiveKit API Chave\",\n \"livekitApiSecret\": \"LiveKit API Secret\",\n \"livekitSipTrunkId\": \"LiveKit SIP Trunk ID\",\n \"livekitRoomName\": \"LiveKit Room Nome\",\n \"livekitRoomNameTooltip\": \"The LiveKit room name to join and wait for SIP calls. Must match the room name in your SIP Dispatch Rule.\",\n \"xiaoyiSetupGuide\": \"Please create an agent on Huawei Developer Platform and get AK/SK and Agent ID. AK/SK can be found in the credential management page.\",\n \"filterAll\": \"Todos\",\n \"builtin\": \"Integrado\",\n \"custom\": \"Personalizado\",\n \"feishuRegion\": \"Region\",\n \"feishuRegionTooltip\": \"Choose Feishu for China or Lark for International\",\n \"feishuChina\": \"Feishu (China)\",\n \"feishuInternational\": \"Lark (International)\",\n \"feishuScanGuide\": \"Scan the QR code with Feishu to create a bot instantly. A Feishu app will be created automatically and App ID / App Secret will be filled in.\",\n \"feishuScanLogin\": \"Scan to Criar Bot\",\n \"feishuGetQrcode\": \"Get Feishu QR Code\",\n \"feishuScanHint\": \"Scan the QR code above with Feishu. App ID and App Secret will be filled in automatically after authorization.\",\n \"feishuAuthSuccess\": \"Feishu bot created. App ID and App Secret have been filled in.\",\n \"feishuQrcodeExpired\": \"QR code expired, please get a new one.\",\n \"feishuQrcodeFailed\": \"Failed to get Feishu QR code. Please try again.\",\n \"wechatSetupGuide\": \"WeChat personal account Bot (iLink protocol). On first start without a Bot Token, a QR code login URL will be printed; scan to authorize. The token is saved locally for future use.\",\n \"wechatContextTokenLimit\": \"WeChat iLink platform limitation: each user message's context_token allows a maximum of 10 reply messages. This is a hard limit enforced by the platform. It is recommended to disable thinking and tool output, or enable Message Merge to avoid exceeding the limit and causing message send failures.\",\n \"wechatBotToken\": \"Bot Token\",\n \"wechatBotTokenTooltip\": \"Bearer token obtained after QR code login. Leave empty to trigger QR code login on startup.\",\n \"wechatBotTokenPlaceholder\": \"Auto-filled after QR login, or paste manually\",\n \"wechatBotTokenFile\": \"Token File Caminho\",\n \"wechatBotTokenFileTooltip\": \"Path to persist the bot token. Defaults to ~/.qwenpaw/wechat_bot_token\",\n \"wechatMediaDir\": \"Media Directory\",\n \"wechatMessageMerge\": \"Message Merge\",\n \"wechatMessageMergeTooltip\": \"Merge multiple reply messages into fewer messages to avoid hitting the WeChat 10-message limit per context_token. When enabled with delay 0, all text replies are merged into a single message sent at the end. A positive delay (in ms) merges adjacent messages within that time window.\",\n \"wechatMessageMergeDelayMs\": \"Merge Delay (ms)\",\n \"wechatMessageMergeDelayMsTooltip\": \"Time window for adjacent message merging. Set to 0 to merge all messages into one final reply. A positive value (e.g. 1000) will buffer messages for that duration before sending.\",\n \"wechatMessageMergeDelayMsValidation\": \"Digite a valid non-negative integer\",\n \"wechatScanLogin\": \"Scan to Login\",\n \"wechatGetQrcode\": \"Get Login QR Code\",\n \"wechatScanHint\": \"Scan the QR code with WeChat, then click Save\",\n \"wechatQrcodeFailed\": \"Failed to fetch QR code, please try again\",\n \"wechatLoginSuccess\": \"WeChat login successful, token has been filled in\",\n \"wechatQrcodeExpired\": \"QR code expired, please get a new one\",\n \"shareSessionInGroup\": \"Compartilhar Contexto em Grupo\",\n \"shareSessionInGroupTooltip\": \"Quando ativado, todos os membros do grupo compartilham o mesmo contexto de conversa. Quando desativado, cada membro tem seu próprio contexto independente.\",\n \"accessControlEnabled\": \"Controle de Acesso\",\n \"accessControlEnabledTooltip\": \"Quando ativado, apenas usuários na lista branca podem interagir com o bot. Usuários desconhecidos receberão seu ID e serão adicionados à lista de aprovação pendente.\",\n \"accessControlDm\": \"Controle de Acesso em DM\",\n \"accessControlDmTooltip\": \"Quando ativado, apenas usuários na lista branca podem interagir com o bot em mensagens diretas\",\n \"accessControlGroup\": \"Controle de Acesso em Grupo\",\n \"accessControlGroupTooltip\": \"Quando ativado, apenas usuários na lista branca podem interagir com o bot em chats de grupo\",\n \"dmDisabled\": \"Desativar DM\",\n \"dmDisabledTooltip\": \"Quando ativado, o bot ignorará completamente todas as mensagens diretas\",\n \"groupDisabled\": \"Desativar Grupo\",\n \"groupDisabledTooltip\": \"Quando ativado, o bot ignorará completamente todas as mensagens de grupo\",\n \"manageAccessControl\": \"Controle de Acesso\",\n \"pendingApprovals\": \"Aprovações Pendentes\",\n \"whitelist\": \"Lista Branca\",\n \"blacklist\": \"Lista Negra\",\n \"addUser\": \"Adicionar\",\n \"addUserPlaceholder\": \"Digite o ID do usuário\",\n \"userId\": \"ID do Usuário\",\n \"username\": \"Nome de usuário\",\n \"channel\": \"Canal\",\n \"firstMessage\": \"Primeira Mensagem\",\n \"time\": \"Hora\",\n \"actions\": \"Ações\",\n \"approve\": \"Lista Branca\",\n \"deny\": \"Lista Negra\",\n \"dismiss\": \"Dispensar\",\n \"noWhitelistUsers\": \"Nenhum usuário na lista branca\",\n \"noBlacklistUsers\": \"Nenhum usuário na lista negra\",\n \"noPendingApprovals\": \"Nenhuma aprovação pendente\",\n \"userAdded\": \"Usuário adicionado\",\n \"userRemoved\": \"Usuário removido\",\n \"approveSuccess\": \"Adicionado à lista branca\",\n \"denySuccess\": \"Adicionado à lista negra\",\n \"dismissSuccess\": \"Dispensado\",\n \"operationFailed\": \"Operação falhou\",\n \"remark\": \"Observação\",\n \"usernamePlaceholder\": \"Digite o nome de usuário (opcional)\",\n \"remarkPlaceholder\": \"Digite uma observação (opcional)\",\n \"filterByChannel\": \"Filtrar por canal\",\n \"selectToOperate\": \"Selecione itens para operar\",\n \"selectedCount\": \"{{count}} selecionados\",\n \"batchApprove\": \"Aprovar\",\n \"batchDeny\": \"Lista negra\",\n \"batchDismiss\": \"Remover\",\n \"batchApproveConfirm\": \"Aprovar {{count}} usuários?\",\n \"batchDenyConfirm\": \"Adicionar {{count}} usuários à lista negra?\",\n \"batchDismissConfirm\": \"Remover {{count}} usuários?\",\n \"batchSuccess\": \"{{count}} usuários processados com sucesso\",\n \"batchRemove\": \"Excluir\",\n \"batchRemoveConfirm\": \"Excluir {{count}} usuários selecionados?\",\n \"enabledSection\": \"Ativados\",\n \"enabledCount\": \"{{count}}\",\n \"availableSection\": \"Disponíveis\",\n \"enableAction\": \"Ativar\",\n \"noEnabledChannels\": \"Nenhum canal ativado\",\n \"goEnableChannels\": \"Ativar canais\"\n },\n \"sessions\": {\n \"title\": \"Sessoes\",\n \"description\": \"View and manage active chat sessions\",\n \"loading\": \"Carregando sessions...\",\n \"confirmDelete\": \"Confirmar Excluir\",\n \"deleteConfirm\": \"Are you sure you want to delete this session?\",\n \"batchDeleteConfirm\": \"Are you sure you want to delete selected {{count}} sessions?\",\n \"deleteSuccess\": \"Session deleted successfully\",\n \"deleteFailed\": \"Falhou to Excluir session\",\n \"batchDeleteSuccess\": \"Successfully deleted {{count}} session(s)\",\n \"batchDeleteFailed\": \"Falha ao batch Excluir Sessoes\",\n \"saveSuccess\": \"Session saved successfully\",\n \"saveFailed\": \"Falhou to Salvar session\",\n \"batchDeleteButton\": \"Batch Excluir\",\n \"filterTitle\": \"Filtrar por Título\",\n \"filterUserId\": \"Filtrar by Usuario ID\",\n \"filterChannel\": \"Filtrar by Channel\",\n \"allChannels\": \"Todos Canais\",\n \"totalItems\": \"Total {{count}} items\",\n \"selectedItems\": \"Selecionado {{count}} items\",\n \"editSession\": \"Editar Session\",\n \"pleaseInputName\": \"Please input session Nome\",\n \"sessionNamePlaceholder\": \"Session Nome\",\n \"activeTab\": \"Ativas\",\n \"archivedTab\": \"Arquivadas\",\n \"archivedAtColumn\": \"Arquivado em\",\n \"archive\": {\n \"action\": \"Arquivar\",\n \"unaction\": \"Desarquivar\",\n \"batchAction\": \"Arquivar em lote\",\n \"batchUnaction\": \"Desarquivar em lote\",\n \"success\": \"Conversa arquivada\",\n \"successHint\": \"Conversa arquivada. Veja em Controle > Sessões > Arquivadas.\",\n \"failed\": \"Falha ao arquivar\",\n \"unarchiveSuccess\": \"Conversa desarquivada\",\n \"unarchiveFailed\": \"Falha ao desarquivar\",\n \"batchSuccess\": \"{{count}} conversa(s) arquivada(s)\",\n \"batchFailed\": \"Falha ao arquivar em lote\",\n \"batchUnarchiveSuccess\": \"{{count}} conversa(s) desarquivada(s)\",\n \"batchUnarchiveFailed\": \"Falha ao desarquivar em lote\",\n \"groupLabel\": \"Arquivadas ({{count}})\",\n \"modalTitle\": \"Conversas Arquivadas\",\n \"unarchiveAll\": \"Desarquivar Todas\",\n \"noArchived\": \"Nenhuma conversa arquivada\"\n }\n },\n \"environments\": {\n \"parent\": \"Configuracoes\",\n \"environments\": \"Variaveis de Ambiente\",\n \"title\": \"Variaveis de Ambiente\",\n \"description\": \"Configure key-value environment variables for agents and skills.\",\n \"key\": \"Chave\",\n \"value\": \"Value\",\n \"variableNamePlaceholder\": \"VARIABLE_NAME\",\n \"valuePlaceholder\": \"value\",\n \"insertRowBelow\": \"Insert row below\",\n \"deleteRow\": \"Excluir row\",\n \"deleteVariable\": \"Excluir Variable\",\n \"deleteConfirm\": \"Excluir \\\"{{name}}\\\"?\",\n \"deleteSelected\": \"Excluir Selecionado\",\n \"deleteSelectedConfirm\": \"Excluir {{label}}?\",\n \"keyRequired\": \"Chave is Obrigatorio\",\n \"invalidKeyFormat\": \"Invalid Chave Formato\",\n \"duplicateKey\": \"Duplicate Chave\",\n \"saveSuccess\": \"Environment variables saved\",\n \"saveFailed\": \"Falhou to Salvar\",\n \"deleteSuccess\": \"\\\"{{name}}\\\" deleted\",\n \"deleteFailed\": \"Falhou to Excluir\",\n \"noVariables\": \"No environment variables configured yet.\",\n \"addVariable\": \"Adicionar Variable\",\n \"loading\": \"Loading…\",\n \"retry\": \"Tentar novamente\",\n \"of\": \"of\",\n \"selected\": \"Selecionado\",\n \"variable\": \"variable\",\n \"variables\": \"variables\",\n \"showValue\": \"Mostrar value\",\n \"hideValue\": \"Ocultar value\"\n },\n \"modelSelector\": {\n \"selectModel\": \"Selecionar Modelo\",\n \"noConfiguredModels\": \"Nenhum modelo configurado\",\n \"noFreeModels\": \"Nenhum modelo gratuito disponível\",\n \"switchFailed\": \"Falha ao trocar modelo\",\n \"searchModels\": \"Buscar modelos...\",\n \"noModelsFound\": \"Nenhum modelo encontrado\",\n \"free\": \"Grátis\",\n \"vision\": \"Visão\",\n \"freeBannerText\": \"Modelos gratuitos podem ter limites de uso e instabilidade. Consulte o provedor para detalhes.\",\n \"moreProviders\": \"Mais provedores gratuitos (requer API Key)\",\n \"proBannerText\": \"Exibindo modelos configurados em Configurações.\",\n \"connectToUse\": \"Conectar {{provider}} para modelos gratuitos\",\n \"configureApiKey\": \"Configurar API Key do {{provider}} para modelos gratuitos\",\n \"configureApiKeyTitle\": \"Ir para Configuração\",\n \"configureApiKeyConfirm\": \"Você será redirecionado para a página de Modelos para configurar a API Key do {{provider}}. Continuar?\",\n \"goToConfigure\": \"Ir para Configurar\",\n \"noModelsDiscovered\": \"Conectado. Carregando modelos...\",\n \"oauthTitle\": \"Conectar ao {{provider}}\",\n \"oauthDescription\": \"Uma nova janela do navegador será aberta para autorizar o acesso ao {{provider}}. A chave API será salva automaticamente.\",\n \"oauthContinue\": \"Continuar\",\n \"oauthWaiting\": \"Aguardando autorização...\",\n \"oauthWaitingDescription\": \"Por favor, conclua a autorização na janela do navegador.\",\n \"oauthConnected\": \"Conectado ao {{provider}} com sucesso\",\n \"oauthFailed\": \"Autorização falhou, tente novamente\",\n \"viewMore\": \"Ver mais ({{count}})\"\n },\n \"tokenUsage\": {\n \"title\": \"Uso de Tokens\",\n \"description\": \"View LLM token consumption over time, by date and model.\",\n \"totalTokens\": \"Total Tokens\",\n \"totalCalls\": \"Total Calls\",\n \"promptTokens\": \"Prompt Tokens\",\n \"completionTokens\": \"Completion Tokens\",\n \"byModel\": \"By Modelo\",\n \"byDate\": \"By Date\",\n \"provider\": \"Provedor\",\n \"model\": \"Modelo\",\n \"date\": \"Date\",\n \"refresh\": \"Atualizar\",\n \"loadFailed\": \"Falha ao load token usage\",\n \"noData\": \"No token usage data in the selected period\"\n },\n \"agentStats\": {\n \"title\": \"Estatisticas dos Agentes\",\n \"description\": \"View agent statistics for the selected time period.\",\n \"totalSessions\": \"Total Sessoes\",\n \"totalMessages\": \"Total Messages\",\n \"userMessages\": \"Usuario Messages\",\n \"assistantMessages\": \"Assistant Messages\",\n \"promptTokens\": \"Prompt Tokens\",\n \"completionTokens\": \"Completion Tokens\",\n \"llmCalls\": \"LLM Calls\",\n \"toolCalls\": \"Tool Calls\",\n \"channel\": \"Channel\",\n \"sessionCount\": \"Sessoes\",\n \"sessions\": \"Sessoes\",\n \"newSessions\": \"Novo Sessoes\",\n \"activeSessions\": \"Ativo Sessoes\",\n \"messages\": \"Messages\",\n \"messageTrend\": \"Message Trend\",\n \"sessionTrend\": \"Session Trend\",\n \"tokenTrend\": \"Token Trend\",\n \"llmAndToolTrend\": \"LLM & Tool Call Trend\",\n \"sessionsByChannel\": \"Sessoes by Channel\",\n \"messagesByChannel\": \"Messages by Channel\",\n \"totalSessionsTooltip\": \"Total chat sessions created, each session is an independent conversation context\",\n \"totalMessagesTooltip\": \"Total messages across all sessions, including both user and assistant messages\",\n \"promptTokensTooltip\": \"Total prompt tokens sent to the LLM\",\n \"completionTokensTooltip\": \"Total completion tokens generated by the LLM\",\n \"llmCallsTooltip\": \"Number of LLM API calls, each request counts as one call\",\n \"toolCallsTooltip\": \"Number of times the agent decided to invoke a tool; a single LLM call may produce multiple tool-use intents\",\n \"messageTrendTooltip\": \"Trend comparing user messages vs assistant messages\",\n \"sessionTrendTooltip\": \"Trend comparing new sessions vs active sessions; orange bars are sessions created that day, blue bars are sessions with messages that day\",\n \"tokenTrendTooltip\": \"Trend of prompt and completion token consumption\",\n \"llmAndToolTrendTooltip\": \"Trend comparing LLM calls vs tool-use intents\",\n \"sessionsByChannelTooltip\": \"Distribution of sessions by messaging channel; sessions without a channel are grouped under Console\",\n \"messagesByChannelTooltip\": \"Distribution of total messages by messaging channel\",\n \"loadFailed\": \"Falhou to load statistics\",\n \"noData\": \"No statistics data in the selected period\",\n \"retry\": \"Tentar novamente\"\n },\n \"models\": {\n \"llmConfiguration\": \"LLM Configuracao\",\n \"providersTitle\": \"Providers\",\n \"providersDescription\": \"Configure API keys and endpoints for each provider.\",\n \"llmTitle\": \"Modelos\",\n \"defaultLlm\": \"Padrao LLM\",\n \"llmDescription\": \"Set the global default LLM model from an authorized provider. You can choose a specific model for each Agent in the Chat page.\",\n \"configureProvider\": \"Configure {{name}}\",\n \"baseURL\": \"URL Base\",\n \"apiKey\": \"Chave da API\",\n \"advancedConfig\": \"Avancado Configuracao\",\n \"authMode\": \"Modo de Autenticação\",\n \"authModeApiKey\": \"API Key (x-api-key)\",\n \"authModeAuthToken\": \"Auth Token (Authorization: Bearer)\",\n \"customHeaders\": \"Cabeçalhos Personalizados\",\n \"customHeadersHint\": \"Adicionados a todas as requisições de API, ex: x-app: cli\",\n \"customHeaderKey\": \"Nome do Cabeçalho\",\n \"customHeaderValue\": \"Valor do Cabeçalho\",\n \"addHeader\": \"+ Adicionar Cabeçalho\",\n \"generateConfig\": \"Generation Parameters\",\n \"generateConfigHint\": \"Generation parameters expressed as a JSON object. They will be expanded and passed into the generation request (`openai.chat.completions` or `anthropic.messages`).\",\n \"generateConfigInvalidJson\": \"Please enter valid JSON\",\n \"generateConfigMustBeObject\": \"Generation parameters must be a JSON object\",\n \"modelAdvancedConfig\": \"Modelo Avancado Configuracao\",\n \"modelGenerateConfig\": \"Modelo Generation Parameters\",\n \"modelGenerateConfigHint\": \"Generation parameters expressed as a JSON object for this model. They will be expanded and passed into the generation request. These override provider-level advanced configuration.\",\n \"modelConfigSaved\": \"Configuration saved for model {{name}}\",\n \"modelConfigSaveFailed\": \"Falha ao Salvar Modelo Configuracao\",\n \"modelConfigLabel\": \"Modelo Configuracao\",\n \"maxTokensLabel\": \"Máx. Tokens\",\n \"maxTokensHint\": \"Número máximo de tokens de saída por resposta\",\n \"maxInputLengthLabel\": \"Comprimento Máximo do Contexto\",\n \"maxInputLengthHint\": \"Tamanho da janela de contexto do modelo, controla o limite de compactação do contexto (≥1000)\",\n \"relayReasoningLabel\": \"Retransmitir Raciocínio\",\n \"relayReasoningHint\": \"Retransmitir o conteúdo de raciocínio para os turnos de conversa subsequentes\",\n \"thinkingModeLabel\": \"Ativar Pensamento\",\n \"thinkingModeHint\": \"Ative para habilitar pensamento/raciocínio. Desative para desabilitar o pensamento.\",\n \"thinkingBudgetLabel\": \"Orçamento de Pensamento\",\n \"thinkingBudgetHint\": \"Usando orçamento de pensamento padrão do modelo\",\n \"switchToManual\": \"Manual\",\n \"switchToAuto\": \"Auto\",\n \"reasoningEffortLabel\": \"Profundidade de Raciocínio\",\n \"reasoningEffortHint\": \"Controla a profundidade do raciocínio (família OpenAI)\",\n \"protocol\": \"Protocol\",\n \"protocolHint\": \"Choose the provider API protocol for this configuration.\",\n \"selectProtocol\": \"Please Selecionar a protocol\",\n \"protocolOpenAI\": \"OpenAI-compatible (Chat Completions)\",\n \"protocolOpenAIResponse\": \"OpenAI-compatible (Response API)\",\n \"protocolAnthropic\": \"Anthropic (Messages API)\",\n \"currentKey\": \"Current: {{key}}\",\n \"startsWith\": \"Starts with \\\"{{prefix}}\\\"\",\n \"optionalSelfHosted\": \"Opcional for self-hosted services\",\n \"leaveBlankKeep\": \"Leave blank to keep current key\",\n \"enterApiKey\": \"Enter API Chave ({{prefix}}...)\",\n \"enterApiKeyOptional\": \"Enter API Chave (optional)\",\n \"openAIEndpoint\": \"OpenAI endpoint, e.g. https://api.openai.com/v1\",\n \"openAICompatibleEndpoint\": \"OpenAI-compatible endpoint, e.g. https://api.example.com (append /v1 only if your provider requires it)\",\n \"azureEndpointHint\": \"Azure OpenAI endpoint, e.g. https://<resource>.openai.azure.com/openai/v1\",\n \"anthropicEndpointHint\": \"Anthropic endpoint, e.g. https://api.anthropic.com\",\n \"ollamaEndpointHint\": \"Ollama endpoint, e.g. http://localhost:11434\",\n \"lmstudioEndpointHint\": \"LM Studio endpoint, e.g. http://localhost:1234/v1\",\n \"apiEndpointHint\": \"API endpoint, e.g. https://api.example.com\",\n \"pleaseEnterBaseURL\": \"Digite the API base URL\",\n \"selectBaseURL\": \"Selecione uma URL base\",\n \"selectBaseURLHint\": \"Selecione o endpoint regional deste provedor\",\n \"pleaseEnterValidURL\": \"Digite a valid URL\",\n \"apiKeyShouldStart\": \"API Key should start with \\\"{{prefix}}\\\"\",\n \"configurationSaved\": \"{{name}} Configuracao saved\",\n \"failedToSaveConfig\": \"Falhou to Salvar Configuracao\",\n \"revokeAuthorization\": \"Revoke Autorizacao\",\n \"revokeConfirmContent\": \"Are you sure you want to remove the API key for {{name}}? The current LLM model configuration will also be cleared.\",\n \"revokeConfirmSimple\": \"Are you sure you want to remove the API key for {{name}}?\",\n \"authorizationRevoked\": \"{{name}} authorization revoked, LLM model cleared\",\n \"authorizationRevokedSimple\": \"{{name}} Autorizacao revoked\",\n \"failedToRevoke\": \"Falhou to revoke Autorizacao\",\n \"active\": \"Active: {{provider}} / {{model}}\",\n \"provider\": \"Provedor\",\n \"model\": \"Modelo\",\n \"selectProvider\": \"Select provider (must be authorized)\",\n \"selectModel\": \"Selecionar a Modelo\",\n \"llmModelUpdated\": \"LLM Modelo Atualizado\",\n \"failedToSave\": \"Falhou to Salvar\",\n \"freeModelWarningTitle\": \"Aviso sobre Modelo Gratuito\",\n \"freeModelWarningMessage\": \"Modelos gratuitos podem ser instáveis. Consulte os termos de serviço do provedor:\",\n \"freeModelWarningDontShowAgain\": \"Do not show this again\",\n \"saved\": \"Saved\",\n \"save\": \"Salvar\",\n \"cancel\": \"Cancelar\",\n \"loading\": \"Carregando...\",\n \"retry\": \"Tentar novamente\",\n \"notSet\": \"Nao definido\",\n \"notRequired\": \"Nao necessario\",\n \"available\": \"Available\",\n \"unavailable\": \"Unavailable\",\n \"providerAvailable\": \"Ready (with models)\",\n \"providerNoModels\": \"Not Ready (no models)\",\n \"providerNotConfigured\": \"Not Ready (not configured)\",\n \"settings\": \"Configuracoes\",\n \"actions\": \"Acoes\",\n \"search\": \"Pesquisar\",\n \"searchPlaceholder\": \"Pesquisar providers...\",\n \"searchModelPlaceholder\": \"Pesquisar models...\",\n \"custom\": \"Personalizado\",\n \"builtin\": \"Integrado\",\n \"freeTierGroup\": \"Gratuito\",\n \"paidGroup\": \"Pago\",\n \"localCustomGroup\": \"Local e Personalizado\",\n \"endpointCount\": \"{{count}} endpoints\",\n \"connect\": \"OAuth\",\n \"saveApiKey\": \"Salvar\",\n \"addProvider\": \"Adicionar Provedor\",\n \"addProviderTitle\": \"Adicionar Personalizado Provedor\",\n \"providerIdLabel\": \"Provedor ID\",\n \"providerIdPlaceholder\": \"e.g. openai, google, anthropic\",\n \"providerIdHint\": \"Lowercase letters, digits, hyphens, underscores. Cannot be changed later.\",\n \"providerNameLabel\": \"Display Nome\",\n \"providerNamePlaceholder\": \"e.g. OpenAI, Google Gemini\",\n \"defaultBaseUrlLabel\": \"Padrao Base URL\",\n \"defaultBaseUrlPlaceholder\": \"e.g. https://api.example.com\",\n \"apiKeyPrefixLabel\": \"API Chave Prefix (optional)\",\n \"apiKeyPrefixPlaceholder\": \"e.g. sk-\",\n \"providerCreated\": \"Provedor \\\"{{name}}\\\" Criado\",\n \"providerCreateFailed\": \"Falhou to Criar Provedor\",\n \"deleteProvider\": \"Excluir Provedor\",\n \"deleteProviderConfirm\": \"Delete custom provider \\\"{{name}}\\\" and all its models? This cannot be undone.\",\n \"providerDeleted\": \"Provedor \\\"{{name}}\\\" deleted\",\n \"providerDeleteFailed\": \"Falhou to Excluir Provedor\",\n \"manageModels\": \"Modelos\",\n \"models\": \"Modelos\",\n \"manageModelsTitle\": \"{{provider}} — Modelo Management\",\n \"userAdded\": \"User-added\",\n \"addModel\": \"Adicionar Modelo\",\n \"addModels\": \"Adicionar Modelos\",\n \"autoDiscoverModels\": \"Automatico Discover Modelos\",\n \"addModelTitle\": \"Adicionar Modelo to {{provider}}\",\n \"modelIdLabel\": \"Modelo ID\",\n \"modelIdPlaceholder\": \"e.g. gpt-4o, gemini-2.0-flash\",\n \"modelDiscoveryUnavailableHint\": \"No available models were found. This provider may not support model list discovery, so please enter the model ID manually.\",\n \"modelNameLabel\": \"Modelo Nome\",\n \"modelNameRequired\": \"Please enter Modelo Nome\",\n \"modelNamePlaceholder\": \"e.g. GPT-4o, Gemini 2.0 Flash\",\n \"modelAdded\": \"Modelo \\\"{{name}}\\\" added\",\n \"modelAddFailed\": \"Falhou to Adicionar Modelo\",\n \"modelAlreadyExists\": \"Modelo ID \\\"{{id}}\\\" ja existe\",\n \"autoDiscoverModelsSuccess\": \"Auto discovery complete. Added {{count}} models\",\n \"autoDiscoverModelsNoNew\": \"Auto discovery complete. No new models were added\",\n \"autoDiscoverModelsFailed\": \"Falha ao Automatico discover Modelos\",\n \"removeModel\": \"Remover\",\n \"removeModelConfirm\": \"Remove model \\\"{{name}}\\\" from {{provider}}?\",\n \"modelRemoved\": \"Modelo \\\"{{name}}\\\" removed\",\n \"modelRemoveFailed\": \"Falhou to Remover Modelo\",\n \"loadMore\": \"Carregar mais ({{count}})\",\n \"modelsCount\": \"{{count}} Modelos\",\n \"noModels\": \"Nao Modelos\",\n \"addModelFirst\": \"Please add a model first\",\n \"modelList\": \"Modelo List\",\n \"clearAll\": \"Limpar Todos\",\n \"filteredModelsLoaded\": \"Filtered Modelos loaded: {{count}}\",\n \"filterFailed\": \"Falhou to Filtrar Modelos\",\n \"clearAllModels\": \"Limpar Todos Modelos\",\n \"clearAllModelsConfirm\": \"Clear all {{count}} extra models? This cannot be undone.\",\n \"allModelsCleared\": \"Cleared {{count}} Modelos\",\n \"filterModels\": \"Filtrar Modelos\",\n \"filterByProvider\": \"Provider:\",\n \"searchProviderPlaceholder\": \"Pesquisar Provedor Nome\",\n \"selectAllProviders\": \"Selecionar Todos\",\n \"clearProviderSelection\": \"Deselect Todos\",\n \"noMatchingProviders\": \"Nao matching providers\",\n \"filterByModality\": \"Input Modality:\",\n \"filterFreeOnly\": \"Free Modelos Only:\",\n \"getModels\": \"Get Modelos\",\n \"discovered\": \"Available Models:\",\n \"add\": \"Adicionar\",\n \"local\": \"Local\",\n \"localType\": \"Tipo\",\n \"localEmbedded\": \"Embedded (in-process)\",\n \"localDownloadFirst\": \"Modelo service not started\",\n \"localDownloadModel\": \"Baixar Modelo\",\n \"localModelsTitle\": \"{{provider}} — Local Modelos\",\n \"localNoModels\": \"Nao downloaded Modelos yet\",\n \"localRepoId\": \"Modelo Repository ID\",\n \"localRepoIdRequired\": \"Digite a Modelo repository ID\",\n \"localRepoIdPlaceholder\": \"e.g. Qwen/Qwen3-0.6B-GGUF\",\n \"localFilename\": \"Filename (optional)\",\n \"localFilenamePlaceholder\": \"e.g. mistral-7b.Q4_K_M.gguf\",\n \"localFilenameHint\": \"Leave empty to auto-select the best quantization\",\n \"localSource\": \"Origem\",\n \"localDownloadSuccess\": \"Modelo downloaded successfully\",\n \"localDownloadFailed\": \"Falhou to Baixar Modelo\",\n \"localDeleteModel\": \"Excluir Modelo\",\n \"localDeleteConfirm\": \"Delete local model \\\"{{name}}\\\"? The model file will be removed from disk.\",\n \"localModelDeleted\": \"Modelo \\\"{{name}}\\\" deleted\",\n \"localDeleteFailed\": \"Falhou to Excluir Modelo\",\n \"localDownloadPending\": \"Preparing to download...\",\n \"localDownloading\": \"Downloading {{repo}}... This may take a few minutes.\",\n \"localDownloadNavigateHint\": \"You can navigate away — the download will continue in the background.\",\n \"localDownloadInProgress\": \"A download is already in progress\",\n \"localDownloaded\": \"Downloaded\",\n \"localNotDownloaded\": \"Not downloaded\",\n \"localNoRecommendedModels\": \"This device is not powerful enough to run local models\",\n \"localCancelDownloadTitle\": \"Cancelar Baixar\",\n \"localCancelDownloadAction\": \"Cancelar Baixar\",\n \"localCancelDownloadConfirm\": \"Cancelar Baixar of \\\"{{repo}}\\\"?\",\n \"localDownloadCancelled\": \"Baixar cancelled\",\n \"localCancelDownloadFailed\": \"Falhou to Cancelar Baixar\",\n \"localLlamacppName\": \"Inferencia engine\",\n \"localLlamacppInstallHint\": \"Install the local runtime before downloading or starting a model.\",\n \"localInstallLlamacpp\": \"Install llama.cpp\",\n \"localLlamacppInstallStarted\": \"llama.cpp Baixar started\",\n \"localLlamacppInstallSuccess\": \"llama.cpp installed successfully\",\n \"localLlamacppInstallFailed\": \"Falhou to install llama.cpp\",\n \"localRuntimeSectionTitle\": \"Inferencia Engine\",\n \"localRuntimeSectionDescription\": \"Powered by Llama.cpp\",\n \"localRuntimeComputeHint\": \"CPU is used by default. For GPU acceleration, use Ollama or LM Studio.\",\n \"localRuntimeInstalled\": \"Installed\",\n \"localRuntimeUpdateAvailable\": \"Atualizar available\",\n \"localRuntimeUpdateAction\": \"Click to download the latest version\",\n \"localRuntimeUpdateConfirmTitle\": \"Confirmar llama.cpp Atualizar\",\n \"localRuntimeUpdateConfirmContent\": \"Updating will overwrite the currently installed llama.cpp version. Confirm to start downloading and installing the latest version.\",\n \"localRuntimeUpdateConfirmContentWithServer\": \"Updating will overwrite the currently installed llama.cpp version and stop the currently running model service ({{model}}). Confirm to start downloading and installing the latest version.\",\n \"localRuntimeUnsupported\": \"Unsupported\",\n \"localRuntimeMissing\": \"Missing\",\n \"localServerRunningTitle\": \"Atual local server\",\n \"localServerOnline\": \"Executando\",\n \"localServerIdle\": \"Dead\",\n \"localServerUnavailable\": \"Issue\",\n \"localNoActiveModel\": \"Nao Ativo Modelo\",\n \"localNoDownloadedModelsHint\": \"Download a suitable model before starting the local inference service.\",\n \"localDownloadModelFirst\": \"Baixar the Modelo first\",\n \"localEngineInstallStateLabel\": \"Install\",\n \"localEngineRunStateLabel\": \"Status\",\n \"localEngineCurrentModelLabel\": \"Atual served Modelo\",\n \"localEngineNextStepLabel\": \"Proximo step\",\n \"localEngineHealthLabel\": \"Engine health\",\n \"localEngineStatusHint\": \"Please download and start a suitable model from the list below\",\n \"localStartServer\": \"Iniciar\",\n \"localStopServer\": \"Parar\",\n \"localServerStartFailed\": \"Falha ao Iniciar local server\",\n \"localServerStopFailed\": \"Falha ao Parar local server\",\n \"localServerSwitchTitle\": \"Switch Executando Modelo\",\n \"localServerSwitchConfirm\": \"The current server is running {{current}}. Switch to {{next}}?\",\n \"localSwitchModel\": \"Switch\",\n \"localModelPathMissing\": \"This downloaded model is missing a local path.\",\n \"localModelsSectionTitle\": \"Local Modelos\",\n \"localModelsSectionDescription\": \"Downloaded models can be started immediately. Only one model download and one local server instance can run at a time.\",\n \"localModelsLockedHint\": \"Please install llama.cpp first\",\n \"localAlternativeRuntimeHint\": \"Please try Ollama or LM Studio\",\n \"localModelsCount\": \"Downloaded {{downloaded}} / {{total}}\",\n \"localCurrentDownloadTitle\": \"Atual Baixar\",\n \"localAdvancedConfigTitle\": \"Avancado Local Configuracoes\",\n \"localAdvancedConfigDescription\": \"These settings apply to the embedded local inference runtime. Max context length changes take effect the next time the local server starts.\",\n \"localAdvancedConfigSaved\": \"Avancado local Configuracoes saved\",\n \"localAdvancedConfigSaveFailed\": \"Falha ao Salvar Avancado local Configuracoes\",\n \"localMaxContextLengthLabel\": \"Max Context Length\",\n \"localMaxContextLengthHint\": \"Maximum token count for the model context window, determining how much history fits in each request. Changes take effect the next time the local server starts.\",\n \"localMaxContextLengthRequired\": \"Digite max context length\",\n \"localMaxContextLengthMin\": \"Max context length must be at least {{min}}\",\n \"localMaxContextLengthPlaceholder\": \"Enter max context length\",\n \"localServerPortLabel\": \"Server Port\",\n \"localServerPortHint\": \"Leave this empty to let llama.cpp bind an available port automatically. Enter a port to reuse that fixed port on future startups. Changes take effect the next time the local server starts.\",\n \"localServerPortRange\": \"Server port must be between {{min}} and {{max}}\",\n \"localServerPortPlaceholder\": \"Leave empty to auto-select a port\",\n \"localGenerateConfigHint\": \"Configure local model generation parameters as a JSON object. These fields will be expanded into subsequent generation requests.\",\n \"localCustomModelTitle\": \"Personalizado Modelo Baixar\",\n \"localCustomModelHint\": \"Enter a model repository ID. GGUF format only.\",\n \"localSourceHuggingFace\": \"Hugging Face\",\n \"localSourceModelScope\": \"ModelScope\",\n \"ollamaModelNamePlaceholder\": \"e.g. mistral:7b, qwen3:8b\",\n \"testConnection\": \"Testar Connection\",\n \"testConnectionSuccess\": \"Connection Testar successful\",\n \"testConnectionFailed\": \"Connection Testar Falhou\",\n \"testConnectionFailedWithMessage\": \"Connection Testar failed: {{message}}\",\n \"testConnectionError\": \"An error occurred while testing connection\",\n \"discoverModels\": \"Discover Modelos\",\n \"discoverModelsFailed\": \"Falhou to discover Modelos\",\n \"ollamaFetchModelsFailed\": \"Failed to fetch Ollama models. Please check the error details.\",\n \"modelTestFailed\": \"Model validation failed, please check if the model ID is correct\",\n \"modelTestFailedConfirm\": \"Model connection test failed: {{message}}. Do you still want to add this model?\",\n \"autoDiscoveredAndAdded\": \"Auto-discovered {{count}} models and added {{added}} new model(s)\",\n \"autoDiscoveredNoNew\": \"Auto-discovered {{count}} models; model list is already up to date\",\n \"autoDiscoverFailed\": \"Automatic model discovery failed; you can add models manually\",\n \"tagImage\": \"Image\",\n \"tagVideo\": \"Video\",\n \"tagTextOnly\": \"Text Only\",\n \"tagMultimodal\": \"Multimodal\",\n \"tagVision\": \"Vision\",\n \"tagText\": \"Text\",\n \"free\": \"Free\",\n \"probeMultimodal\": \"Testar Multimodal\",\n \"probeImage\": \"Image\",\n \"probeVideo\": \"Video\",\n \"probeSupported\": \"Supported: {{types}}\",\n \"probeNotSupported\": \"This model does not support multimodal input\",\n \"probeFailed\": \"Probe Falhou\",\n \"tagNotProbed\": \"Not Probed\",\n \"modalityVision\": \"Image\",\n \"modalityAudio\": \"Audio\",\n \"modalityVideo\": \"Video\",\n \"modalityFile\": \"File\",\n \"modalityText\": \"Text only\",\n \"perMillionIn\": \"/1M in\",\n \"perMillionOut\": \"/1M out\"\n },\n \"agentConfig\": {\n \"title\": \"Configuracao\",\n \"description\": \"Configure Agente runtime parameters\",\n \"reactAgentTitle\": \"React Agente\",\n \"llmRetryTitle\": \"LLM Automatico Tentar novamente\",\n \"llmRetryEnabled\": \"Ativar Automatico Tentar novamente\",\n \"llmRetryEnabledTooltip\": \"Automatically retry transient LLM API failures such as rate limits, timeouts, or connection errors. Applies to new requests after saving.\",\n \"llmMaxRetries\": \"Max Retries\",\n \"llmMaxRetriesTooltip\": \"Maximum number of extra retry attempts for a retryable LLM request failure.\",\n \"llmMaxRetriesPlaceholder\": \"Enter max retries\",\n \"llmMaxRetriesRequired\": \"Max retries is Obrigatorio\",\n \"llmMaxRetriesMin\": \"Max retries must be at least 1\",\n \"llmBackoffBase\": \"Backoff Base Delay (s)\",\n \"llmBackoffBaseTooltip\": \"Base delay before the first retry. Later retries use exponential backoff from this value.\",\n \"llmBackoffBasePlaceholder\": \"Enter base delay\",\n \"llmBackoffBaseRequired\": \"Backoff base delay e obrigatorio\",\n \"llmBackoffBaseMin\": \"Backoff base delay must be at least 0.1 seconds\",\n \"llmBackoffCap\": \"Backoff Max Delay (s)\",\n \"llmBackoffCapTooltip\": \"Maximum cap for exponential retry backoff delays.\",\n \"llmBackoffCapPlaceholder\": \"Enter max delay\",\n \"llmBackoffCapRequired\": \"Backoff max delay e obrigatorio\",\n \"llmBackoffCapMin\": \"Backoff max delay must be at least 0.5 seconds\",\n \"llmBackoffCapGteBase\": \"Backoff max delay must be greater than or equal to the base delay\",\n \"llmRateLimiterTitle\": \"LLM Rate Limiter\",\n \"llmMaxConcurrent\": \"Max Concurrent Calls\",\n \"llmMaxConcurrentTooltip\": \"Maximum number of LLM calls allowed in-flight at the same time. Shared across all agents; only the first initialization takes effect until restart.\",\n \"llmMaxConcurrentPlaceholder\": \"Enter max concurrent calls\",\n \"llmMaxConcurrentRequired\": \"Max concurrent calls e obrigatorio\",\n \"llmMaxConcurrentRange\": \"Max concurrent calls must be at least 1\",\n \"llmMaxQpm\": \"Max QPM (Queries Per Minute)\",\n \"llmMaxQpmTooltip\": \"Maximum requests allowed within a 60-second sliding window. Requests exceeding this limit wait before being dispatched — proactively preventing 429 rate-limit errors. 0 = disabled.\",\n \"llmMaxQpmPlaceholder\": \"Enter max QPM (0 = unlimited)\",\n \"llmMaxQpmRequired\": \"Max QPM is Obrigatorio\",\n \"llmMaxQpmRange\": \"Max QPM must be at least 0\",\n \"llmRateLimitPause\": \"Rate Limite Pause (s)\",\n \"llmRateLimitPauseTooltip\": \"Default pause duration (seconds) applied globally when a 429 rate-limit response is received. Overridden by the API's Retry-After header when present.\",\n \"llmRateLimitPausePlaceholder\": \"Enter pause duration\",\n \"llmRateLimitPauseRequired\": \"Rate Limite pause e obrigatorio\",\n \"llmRateLimitPauseMin\": \"Rate limit pause must be at least 1 second\",\n \"llmRateLimitJitter\": \"Rate Limite Jitter (s)\",\n \"llmRateLimitJitterTooltip\": \"Random jitter range (seconds) added on top of the pause so concurrent waiters stagger their wake-up and avoid a new burst.\",\n \"llmRateLimitJitterPlaceholder\": \"Enter jitter range\",\n \"llmRateLimitJitterRequired\": \"Rate Limite jitter e obrigatorio\",\n \"llmRateLimitJitterMin\": \"Rate limit jitter must be at least 0 seconds\",\n \"llmAcquireTimeout\": \"Slot Acquire Tempo limite (s)\",\n \"llmAcquireTimeoutTooltip\": \"Maximum time (seconds) a caller will wait to acquire a rate-limiter slot before giving up with an error.\",\n \"llmAcquireTimeoutPlaceholder\": \"Enter acquire Tempo limite\",\n \"llmAcquireTimeoutRequired\": \"Slot acquire Tempo limite e obrigatorio\",\n \"llmAcquireTimeoutMin\": \"Slot acquire timeout must be at least 10 seconds\",\n \"llmAcquireTimeoutGtPauseJitter\": \"Slot acquire timeout must be greater than rate limit pause + jitter\",\n \"contextManagementTitle\": \"Context Management\",\n \"memoryManagerBackend\": \"Backend de gerenciamento de memória de longo prazo\",\n \"memoryManagerBackendTooltip\": \"Backend para o gerenciador de memória. Escolha remelight (arquivos locais) ou adbpg (banco de dados na nuvem).\",\n \"memoryManagerBackendRestartWarning\": \"A troca do backend de gerenciamento de memória de longo prazo não permite recarregamento a quente. Salve e reinicie o QwenPaw para aplicar as alterações.\",\n \"maxIters\": \"Max Iterations\",\n \"maxItersTooltip\": \"Maximum number of reasoning-acting iterations for ReAct agent\",\n \"maxItersPlaceholder\": \"Enter max iterations\",\n \"maxItersRequired\": \"Max iterations is Obrigatorio\",\n \"maxItersMin\": \"Max iterations must be at least 1\",\n \"agentLoopTitle\": \"Configurações de Loop do Agente\",\n \"loopMode\": {\n \"builtIn\": \"Integrado\",\n \"builtInNote\": \"Pipeline bloqueado · Valores editáveis\",\n \"templateSectionTitle\": \"Modelos de Loop\",\n \"templateSectionDescription\": \"Escolha um modelo integrado ou adicione o seu.\",\n \"defaultTab\": \"Padrão\",\n \"goalTab\": \"Meta\",\n \"missionTab\": \"Missão\",\n \"defaultDescription\": \"Loop ReAct protegido padrão usado fora de um modo explícito.\",\n \"goalDescription\": \"Loop persistente e limitado para metas verificáveis.\",\n \"missionDescription\": \"Pipeline persistente para missões longas e com várias etapas.\",\n \"gatePipeline\": \"Pipeline de Gates\",\n \"repetitionTitle\": \"Proteção contra repetição\",\n \"repetitionDescription\": \"Detecta chamadas repetidas e intervém.\",\n \"iterationTitle\": \"Limite de iterações\",\n \"iterationDescription\": \"Limita o número de iterações ReAct.\",\n \"retryTitle\": \"Verificação qualitativa de conclusão\",\n \"retryDescription\": \"Acionada somente quando o agente produz uma resposta de texto sem chamadas de ferramentas.\",\n \"goalTurnTitle\": \"Limite de turnos da meta\",\n \"goalTurnDescription\": \"Limita os turnos em uma meta ativa.\",\n \"maxGoalTurns\": \"Máximo de turnos da meta\",\n \"goalBudgetTitle\": \"Orçamento de tokens da meta\",\n \"goalBudgetDescription\": \"Para quando a meta completa atinge o orçamento.\",\n \"maxGoalTokens\": \"Máximo de tokens da meta\",\n \"goalCompletionTitle\": \"Verificação de conclusão da meta\",\n \"goalCompletionDescription\": \"Lê o status explícito da meta antes de parar.\",\n \"goalCompletionReadOnly\": \"A conclusão é controlada pelo protocolo integrado de status da meta.\",\n \"missionProgressTitle\": \"Verificação do progresso da missão\",\n \"missionProgressDescription\": \"Continua até as histórias passarem ou atingir o limite.\",\n \"missionProgressReadOnly\": \"A conclusão é controlada pelo status integrado das Stories da Mission.\",\n \"missionIterationTitle\": \"Limite de iterações da Mission\",\n \"missionIterationDescription\": \"Defina o limite de execução padrão para novas Missions.\",\n \"workerAttemptsTitle\": \"Tentativas do Worker\",\n \"workerAttemptsDescription\": \"Limite as novas tentativas do Worker após uma Story falhar na verificação.\",\n \"missionVerificationTitle\": \"Política de verificação da Mission\",\n \"missionVerificationDescription\": \"Defina a orientação de verificação e o comando de teste padrão.\",\n \"defaultMaxMissionIterations\": \"Máximo padrão de rodadas de execução\",\n \"defaultMaxMissionIterationsTooltip\": \"Usado quando /mission não especifica --max-iterations.\",\n \"maxRetriesPerStory\": \"Máximo de tentativas por Story\",\n \"maxRetriesPerStoryTooltip\": \"Tentativas do Worker após resultado FAIL ou PARTIAL do Verifier.\",\n \"defaultVerificationInstructions\": \"Orientações de verificação (opcional)\",\n \"defaultVerificationInstructionsHelp\": \"Orientações em linguagem natural aplicadas a cada Mission Verifier.\",\n \"defaultVerificationInstructionsPlaceholder\": \"Por exemplo: verifique caminhos do Windows e a interface renderizada.\",\n \"defaultVerifyCommand\": \"Comando de teste padrão (opcional)\",\n \"defaultVerifyCommandHelp\": \"O Verifier tenta executá-lo com ferramentas e permissões normais. /mission --verify substitui o valor.\",\n \"defaultVerifyCommandPlaceholder\": \"Por exemplo: pytest -q\",\n \"enableRepetitionProtection\": \"Ativar proteção contra repetição\",\n \"removeRule\": \"Remover regra\",\n \"custom\": \"Personalizado\",\n \"customDescription\": \"Crie um pipeline salvo com Gates integrados do QwenPaw.\",\n \"duplicate\": \"Duplicar\",\n \"delete\": \"Excluir\",\n \"displayName\": \"Nome de exibição\",\n \"duplicateModeName\": \"Já existe um modo de Loop com este nome.\",\n \"slashCommand\": \"Comando com barra\",\n \"description\": \"Descrição\",\n \"availableToAgent\": \"Disponível para este agente\",\n \"runsTopToBottom\": \"Executa de cima para baixo\",\n \"addGate\": \"Adicionar Gate\",\n \"emptyPipeline\": \"Adicione ao menos um Gate para disponibilizar este modo.\",\n \"iterationGateTitle\": \"Limite de iterações\",\n \"iterationGateDescription\": \"Para após um número fixo de iterações do Loop.\",\n \"doomGateTitle\": \"Proteção contra repetição\",\n \"doomGateDescription\": \"Detecta chamadas repetidas e muda a estratégia.\",\n \"tokenGateTitle\": \"Orçamento de tokens\",\n \"tokenGateDescription\": \"Limita o uso de tokens de entrada e saída.\",\n \"timeoutGateTitle\": \"Limite de tempo do Loop\",\n \"timeoutGateDescription\": \"Para no próximo limite do Loop após o tempo, sem interromper o trabalho em execução.\",\n \"toolBudgetGateTitle\": \"Orçamento de chamadas de ferramentas\",\n \"toolBudgetGateDescription\": \"Limita todas as chamadas e ferramentas selecionadas.\",\n \"retryGateTitle\": \"Revisão de parada antecipada\",\n \"retryGateDescription\": \"Pede ao agente para verificar antes de encerrar.\",\n \"rubricGateTitle\": \"Rubrica de conclusão\",\n \"rubricGateDescription\": \"Avalia critérios explícitos e revisa quando necessário.\",\n \"qualitativeRubricTitle\": \"Verificação qualitativa de conclusão\",\n \"qualitativeRubricDescription\": \"Verifica respostas de texto sem chamadas de ferramentas usando critérios em linguagem natural.\",\n \"qualitativeRubric\": \"Critérios de conclusão em linguagem natural\",\n \"qualitativeRubricHelp\": \"Acionada somente para respostas de texto sem chamadas de ferramentas; o agente continua se ainda houver trabalho.\",\n \"completionRubricTitle\": \"Verificação do sinal de conclusão\",\n \"completionRubricDescription\": \"Verifica o sinal de conclusão em respostas de texto sem chamadas de ferramentas.\",\n \"completionRubricHelp\": \"Acionada após respostas de texto sem chamadas de ferramentas. O agente produz o sinal de conclusão quando termina; caso contrário, continua trabalhando até atingir o máximo de rodadas de avaliação.\",\n \"completionPrompt\": \"Prompt de verificação da conclusão\",\n \"completionPromptHelp\": \"Descreva quando o resultado está completo. O QwenPaw injeta automaticamente a instrução de saída do sinal de conclusão.\",\n \"completionSignal\": \"Sinal de conclusão\",\n \"completionSignalHelp\": \"O QwenPaw injeta este sinal e a instrução para produzir somente ele no prompt de verificação. Somente uma correspondência exata, sem diferenciar maiúsculas e após remover espaços, encerra o Loop.\",\n \"removeCriterion\": \"Remover critério {{number}}\",\n \"totalTokensHelp\": \"Limite combinado de tokens de entrada e saída usados neste turno.\",\n \"promptTokensHelp\": \"Inclui instruções do sistema, contexto da conversa, esquemas e resultados de ferramentas.\",\n \"completionTokensHelp\": \"Limite de tokens gerados pelo modelo neste turno.\",\n \"maxIterations\": \"Máximo de iterações\",\n \"historyWindow\": \"Janela de histórico\",\n \"similarityThreshold\": \"Limite de similaridade\",\n \"totalTokens\": \"Máximo de tokens totais\",\n \"promptTokens\": \"Máximo de tokens de entrada\",\n \"completionTokens\": \"Máximo de tokens de saída\",\n \"maxSeconds\": \"Máximo de segundos\",\n \"maxSecondsHelp\": \"O tempo é verificado após a resposta do modelo ou chamada de ferramenta atual terminar. O trabalho em execução não é interrompido.\",\n \"allToolCalls\": \"Todas as chamadas de ferramentas\",\n \"perToolLimits\": \"Limites por ferramenta\",\n \"toolName\": \"Nome da ferramenta\",\n \"callLimitAria\": \"Limite de chamadas de {{name}}\",\n \"removeToolLimitAria\": \"Remover limite de {{name}}\",\n \"addToolLimit\": \"Adicionar limite por ferramenta\",\n \"maxRetries\": \"Máximo de tentativas\",\n \"retryInstruction\": \"Instrução de nova tentativa\",\n \"passThreshold\": \"Limite de aprovação\",\n \"maxEvaluations\": \"Máximo de rodadas de avaliação\",\n \"graderError\": \"Erro do avaliador\",\n \"stopSafely\": \"Parar com segurança\",\n \"verifyOnceMore\": \"Verificar mais uma vez\",\n \"completionCriteria\": \"Critérios de conclusão\",\n \"criterionPlaceholder\": \"Descreva uma conclusão observável\",\n \"required\": \"Obrigatório\",\n \"weight\": \"Peso\",\n \"addCriterion\": \"Adicionar critério\",\n \"moveGateAria\": \"Mover {{title}}\",\n \"removeGate\": \"Remover Gate\",\n \"newModeName\": \"Novo modo de Loop\",\n \"defaultCustomDescription\": \"Um pipeline de Gates personalizado.\",\n \"copyName\": \"Cópia de {{name}}\",\n \"createModeAria\": \"Criar modo de Loop personalizado\",\n \"createModeTitle\": \"Criar modo de Loop personalizado\",\n \"startingTemplate\": \"Modelo inicial\",\n \"startingPipeline\": \"Pipeline inicial\",\n \"blankPipelineHint\": \"Comece vazio e adicione gates depois\",\n \"safeTemplate\": \"Execução segura\",\n \"researchTemplate\": \"Pesquisa com orçamento\",\n \"qualityTemplate\": \"Qualidade primeiro\",\n \"blankTemplate\": \"Pipeline vazio\"\n },\n \"doomLoopEnabled\": \"Proteção contra Repetição\",\n \"doomLoopEnabledTooltip\": \"Intervir automaticamente quando o agente fica preso repetindo as mesmas ações\",\n \"doomLoopAfter\": \"Após\",\n \"doomLoopRepetitions\": \"ações idênticas\",\n \"doomLoopStopAction\": \"Pausar e Pedir Ajuda\",\n \"doomLoopWarnAction\": \"Enviar Lembrete\",\n \"doomLoopSimpleMode\": \"Simples\",\n \"doomLoopAdvancedMode\": \"Avançado\",\n \"doomLoopWindowSize\": \"Alcance de Detecção\",\n \"doomLoopWindowSizeTooltip\": \"Quantas ações recentes verificar para repetição\",\n \"doomLoopSimilarity\": \"Sensibilidade de Correspondência\",\n \"doomLoopSimilarityTooltip\": \"Quão similares as ações devem ser para contar como repetição (menor = mais rigoroso)\",\n \"doomLoopStages\": \"Regras de Intervenção\",\n \"doomLoopAction\": \"Ação\",\n \"doomLoopPrompt\": \"Mensagem\",\n \"doomLoopPromptPlaceholder\": \"Mensagem de lembrete ou motivo da pausa...\",\n \"doomLoopAddStage\": \"Adicionar Regra\",\n \"iterationTitle\": \"Limite de Iteração\",\n \"iterationEnabled\": \"Ativar Limite de Iteração\",\n \"iterationEnabledTooltip\": \"Parar o agente após um número fixo de turnos de loop\",\n \"iterationMaxIterations\": \"Iterações Máximas\",\n \"iterationMaxIterationsTooltip\": \"Número máximo de turnos de loop antes de parar\",\n \"rubricTitle\": \"Verificação Qualitativa de Conclusão\",\n \"rubricEnabled\": \"Ativar Verificação Qualitativa de Conclusão\",\n \"rubricEnabledTooltip\": \"Acionada somente quando o agente produz uma resposta de texto sem chamadas de ferramentas.\",\n \"rubricPrompt\": \"Critérios de Conclusão em Linguagem Natural\",\n \"rubricPromptTooltip\": \"O prompt injetado para pedir ao agente que avalie a conclusão da tarefa\",\n \"rubricDesc\": \"Aplica critérios de conclusão em linguagem natural a respostas de texto sem chamadas de ferramentas e pede ao agente para continuar se ainda houver trabalho.\",\n \"rubricPromptPlaceholder\": \"Você não chamou nenhuma ferramenta. Se a tarefa está completa, confirme. Caso contrário, continue com chamadas de ferramentas.\",\n \"rubricMaxInterventions\": \"Máximo de Rodadas de Avaliação\",\n \"rubricMaxInterventionsTooltip\": \"Máximo de verificações de conclusão para respostas de texto sem chamadas de ferramentas em um turno.\",\n \"inLoopModes\": \"Ativar nos modos /goal e /mission\",\n \"inLoopModesTooltip\": \"Também aplicar quando o modo /goal ou /mission estiver ativo\",\n \"comingSoonEditable\": \"Configuração personalizada estará disponível em uma versão futura.\",\n \"simpleMode\": \"Simples\",\n \"advancedMode\": \"Avançado\",\n \"comingSoon\": \"Em breve\",\n \"reactModeTab\": \"Modelo Loop - Padrão\",\n \"goalModeTab\": \"Modelo Loop - Goal\",\n \"missionModeTab\": \"Modelo Loop - Mission\",\n \"goalModeInfoTitle\": \"Modo Goal vs Padrão\",\n \"goalModeInfo\": \"O modo padrão para após produzir uma resposta. O modo Goal mantém o agente em loop em direção a um objetivo, usando avaliação Rubric para determinar a conclusão. Todas as operações são executadas no contexto atual do agente.\",\n \"missionModeInfoTitle\": \"Modo Mission vs Goal\",\n \"missionModeInfo\": \"O modo Mission auto-decompõe tarefas complexas em subtarefas, executadas por sub-agentes Worker independentes e verificadas por sub-agentes Verifier. Cada subtarefa é executada em seu próprio contexto, evitando poluição da sessão principal.\",\n \"goalIterationGate\": \"Gate de Iteração de Objetivo\",\n \"goalIterationGateDesc\": \"Limita o número de turnos do agente em uma sessão de objetivo, rastreando iterações e uso de tokens.\",\n \"goalBudgetGate\": \"Gate de Orçamento de Tokens\",\n \"goalBudgetGateDesc\": \"Define um limite de gasto de tokens para a sessão de objetivo. Para o agente quando o orçamento é excedido.\",\n \"goalRubricGate\": \"Rubrica de Conclusão de Objetivo\",\n \"goalRubricGateDesc\": \"Avalia se o objetivo foi alcançado verificando o status da sessão.\",\n \"goalDoomGate\": \"Proteção contra Repetição\",\n \"goalDoomGateDesc\": \"Detecta padrões repetitivos durante a execução do objetivo e aciona intervenção.\",\n \"missionProgressGate\": \"Gate de Progresso da Missão\",\n \"missionProgressGateDesc\": \"Rastreia a conclusão das user stories do PRD. Continua até que todas passem ou o máximo de iterações seja atingido.\",\n \"missionIterBypass\": \"Bypass de Iteração\",\n \"missionIterBypassDesc\": \"Remove temporariamente o limite de iteração do ReAct durante a execução da missão para permitir fases longas.\",\n \"customLoopTitle\": \"Modos de Loop Personalizados\",\n \"customLoopDesc\": \"Crie seus próprios modos de loop com combinações personalizadas de gates. Em breve.\",\n \"autoContinueOnTextOnly\": \"Auto-continue Ligado text-only step\",\n \"autoContinueOnTextOnlyTooltip\": \"If the model ends a reasoning step with only text (no tool calls), inject a hint and run one extra reasoning pass with the same tool policy so multi-step tasks are less likely to stop silently. Turn off to match legacy behavior. May increase token usage when enabled.\",\n \"shellCommandTimeout\": \"Shell Command Tempo limite (s)\",\n \"shellCommandTimeoutTooltip\": \"Default timeout in seconds for the execute_shell_command tool. The LLM can still override this per-call via the timeout parameter. Useful for long-running tasks like compilation or data processing.\",\n \"shellCommandTimeoutPlaceholder\": \"Enter Tempo limite\",\n \"shellCommandTimeoutRequired\": \"Shell command Tempo limite e obrigatorio\",\n \"shellCommandTimeoutMin\": \"Shell command timeout must be at least 1 second\",\n \"shellCommandExecutable\": \"Shell Executável\",\n \"shellCommandExecutableTooltip\": \"Caminho para o shell usado pelo execute_shell_command. Linux/macOS: ex. /bin/bash, /bin/zsh. Windows: suporta powershell.exe, pwsh.exe ou shells POSIX como Git Bash. Quando vazio, usa a variável de ambiente $SHELL, depois o padrão da plataforma (/bin/sh no Unix, cmd.exe no Windows).\",\n \"shellCommandExecutablePlaceholder\": \"ex.: /bin/bash ou powershell.exe (vazio = detecção automática)\",\n \"maxInputLength\": \"Max Input Length\",\n \"maxInputLengthTooltip\": \"Maximum input length (tokens) for the model context window\",\n \"maxInputLengthPlaceholder\": \"Enter max input length\",\n \"maxInputLengthRequired\": \"Max input length e obrigatorio\",\n \"maxInputLengthMin\": \"Max input length must be at least 1000\",\n \"contextCompactRatio\": \"Compaction Threshold Ratio\",\n \"contextCompactRatioTooltip\": \"Compaction is triggered when the context length reaches this fraction of the max context length\",\n \"contextCompactRatioRequired\": \"Context compact ratio e obrigatorio\",\n \"contextCompactThreshold\": \"Compaction Threshold (tokens)\",\n \"contextCompactThresholdTooltip\": \"Auto-calculated from max context length × compaction ratio. Compaction triggers when context exceeds this value.\",\n \"contextCompactThresholdPlaceholder\": \"Auto-calculated\",\n \"contextCompactReserveRatio\": \"Reserve Threshold Ratio\",\n \"contextCompactReserveRatioTooltip\": \"Calculates the target recent-tail budget. Scroll also applies a minimum recent budget and a 40K cap, and always keeps the complete active turn.\",\n \"contextCompactReserveRatioRequired\": \"Context reserve ratio e obrigatorio\",\n \"contextCompactReserveThreshold\": \"Reserve Threshold (tokens)\",\n \"contextCompactReserveThresholdTooltip\": \"The effective target used to split the recent tail. This is a soft target; boundary messages and the complete active turn may increase the retained amount.\",\n \"contextCompactReserveThresholdPlaceholder\": \"Auto-calculated\",\n \"toolResultCompactRecentN\": \"Recent Tool Results Range\",\n \"toolResultCompactRecentNTooltip\": \"Number of recent tool-result-bearing messages that use the larger recent preview limit in Native context mode\",\n \"toolResultCompactRecentNRequired\": \"Recent tool results range e obrigatorio\",\n \"toolResultCompactOldThreshold\": \"Older Tool Result Preview Bytes\",\n \"toolResultCompactOldThresholdTooltip\": \"Maximum preview bytes for older tool results in Native context mode.\",\n \"toolResultCompactOldThresholdPlaceholder\": \"Enter preview byte limit\",\n \"toolResultCompactOldThresholdRequired\": \"Older tool result preview bytes is required\",\n \"toolResultCompactRecentThreshold\": \"Recent Tool Result Preview Bytes\",\n \"toolResultCompactRecentThresholdTooltip\": \"Maximum preview bytes before a tool result enters agent context. In Scroll mode, this also limits each recall_history page and read_file response.\",\n \"toolResultCompactRecentThresholdPlaceholder\": \"Enter preview byte limit\",\n \"toolResultCompactRecentThresholdRequired\": \"Recent tool result preview bytes is required\",\n \"toolResultCompactRetentionDays\": \"Complete Tool Result Retention Days\",\n \"toolResultCompactRetentionDaysTooltip\": \"Independent retention period for complete tool result artifacts. After cleanup, history.db may still retain a bounded preview.\",\n \"toolResultCompactRetentionDaysRequired\": \"Complete tool result retention days is required\",\n \"historyRetentionDays\": \"Dias de Retenção do Histórico\",\n \"historyRetentionDaysTooltip\": \"Dias de histórico de conversa persistente a manter. Linhas mais antigas são removidas automaticamente na inicialização e no encerramento do agente.\",\n \"historyRetentionDaysRequired\": \"Os dias de retenção do histórico são obrigatórios\",\n \"historyRetentionDaysForeverWarning\": \"Definir como 0 mantém o histórico para sempre — o armazenamento crescerá sem limite.\",\n \"historyRetentionDaysLargeWarning\": \"Um período de retenção grande pode consumir bastante armazenamento.\",\n \"exemptFileExtensions\": \"Exempt File Extensions\",\n \"exemptFileExtensionsTooltip\": \"In Native context mode, read_file results for these extensions always use the larger recent preview limit\",\n \"exemptFileExtensionsPlaceholder\": \".md, .txt, .json\",\n \"exemptToolNames\": \"Exempt Tool Names\",\n \"exemptToolNamesTooltip\": \"In Native context mode, results from these tools always use the larger recent preview limit\",\n \"exemptToolNamesPlaceholder\": \"chat_with_agent, list_agents\",\n \"language\": \"Agente Idioma\",\n \"languageTooltip\": \"Language for agent persona files (SOUL.md, AGENTS.md, etc.). Changing this will re-copy MD files in the selected language.\",\n \"languageConfirmTitle\": \"Change Agente Idioma\",\n \"languageConfirmContent\": \"Changing the language will overwrite the following files with the new language version:\\n\\n SOUL.md, AGENTS.md, PROFILE.md, MEMORY.md, BOOTSTRAP.md, HEARTBEAT.md\\n\\nIf you have customized any of these files, please back them up first. Your other files will not be affected.\",\n \"languageConfirmOk\": \"Change Idioma\",\n \"languageSaveSuccess\": \"Idioma Atualizado successfully\",\n \"languageSaveSuccessWithFiles\": \"Language updated, {{count}} MD file(s) copied\",\n \"languageSaveFailed\": \"Falhou to Atualizar Idioma\",\n \"saveSuccess\": \"Configuracao saved successfully\",\n \"saveFailed\": \"Falhou to Salvar Configuracao\",\n \"timezone\": \"Usuario Timezone\",\n \"timezoneTooltip\": \"Used for scheduled tasks, time display, and agent context. Defaults to your system timezone.\",\n \"timezoneRequired\": \"Please Selecionar a timezone\",\n \"selectTimezone\": \"Selecionar timezone\",\n \"timezoneSaveSuccess\": \"Timezone Atualizado successfully\",\n \"timezoneSaveFailed\": \"Falhou to Atualizar timezone\",\n \"loadFailed\": \"Falhou to load Configuracao\",\n \"historyMaxLength\": \"Max History Length\",\n \"historyMaxLengthTooltip\": \"Maximum output length (characters) for the /history command\",\n \"historyMaxLengthRequired\": \"Max history length e obrigatorio\",\n \"historyMaxLengthMin\": \"Max history length must be at least 1000\",\n \"historyMaxLengthPlaceholder\": \"Enter max length\",\n \"contextCompactTitle\": \"Context Compact Configuracao\",\n \"contextCompactEnabled\": \"Ativar Context Compaction\",\n \"contextCompactEnabledTooltip\": \"Automatically compact the context to prevent context window overflow\",\n \"tokenCountModel\": \"Token Count Modelo\",\n \"tokenCountModelTooltip\": \"Model name used for precise token counting; set to \\\"default\\\" to use built-in estimation\",\n \"tokenCountModelPlaceholder\": \"Modelo Nome or Padrao\",\n \"tokenCountUseMirror\": \"Use HuggingFace Mirror\",\n \"tokenCountUseMirrorTooltip\": \"Use a HuggingFace mirror site when downloading tokenizers\",\n \"tokenCountEstimateDivisor\": \"byte/Token Estimate Divisor\",\n \"tokenCountEstimateDivisorTooltip\": \"Divisor for byte-based token estimation (tokens ≈ byte_length / divisor). Recommended value: 3.75 ~ 4.\",\n \"tokenCountEstimateDivisorRequired\": \"Estimate divisor is Obrigatorio\",\n \"tokenCountEstimateDivisorMin\": \"Estimate divisor must be between 2 and 5\",\n \"toolResultCompactTitle\": \"Tool Result Compact Configuracao\",\n \"toolResultCompactEnabled\": \"Ativar Tool Result Compaction\",\n \"toolResultCompactEnabledTooltip\": \"Compact oversized tool call results to save context space\",\n \"memorySummaryTitle\": \"Long-term Memory Configuracao\",\n \"memorySummaryEnabled\": \"Activate Memory Summary\",\n \"memorySummaryEnabledTooltip\": \"Automatically generate and save memory summaries during context compaction\",\n \"dreamCronEnabled\": \"Ativar Dream\",\n \"dreamCronEnabledTooltip\": \"Ativa a tarefa agendada de otimização de memória por Dream\",\n \"dreamCron\": \"Agendamento do Dream\",\n \"dreamCronTooltip\": \"Expressão Cron para otimizar periodicamente memórias de longo prazo; use o interruptor acima para ativar ou desativar\",\n \"dreamCronPlaceholder\": \"0 23 * * *\",\n \"dreamCronRequired\": \"Insira uma expressão Cron quando o Dream estiver ativado\",\n \"dreamCronInvalid\": \"Insira uma expressão Cron válida de 5 campos\",\n \"autoMemorySearch\": \"Busca automática na memória (Beta)\",\n \"autoMemorySearchTooltip\": \"Auto memory search to run on every conversation turn. Each query will automatically invoke memory search capabilities, helping QwenPaw understand you better — but this increases token consumption.\",\n \"autoMaxResults\": \"Automatico Pesquisar Max Results\",\n \"autoMaxResultsTooltip\": \"Maximum number of results returned when auto memory search is enabled\",\n \"autoMaxResultsRequired\": \"Max results is Obrigatorio\",\n \"autoMaxResultsMin\": \"Max results must be at least 1\",\n \"embeddingConfigTitle\": \"Embedding Modelo Configuracao\",\n \"embeddingBackend\": \"Tipo de SDK de Embedding\",\n \"embeddingBackendTooltip\": \"Tipo de SDK usado para chamar o modelo de embedding\",\n \"embeddingBackendPlaceholder\": \"Selecione um tipo de SDK\",\n \"embeddingApiKey\": \"Chave da API\",\n \"embeddingApiKeyTooltip\": \"API key for the embedding provider; leave blank if no authentication is required\",\n \"embeddingApiKeyPlaceholder\": \"Leave blank if not required\",\n \"embeddingBaseUrl\": \"URL Base\",\n \"embeddingBaseUrlTooltip\": \"URL Base da API de embedding. Para backends compatíveis com OpenAI, este endpoint customizado é opcional; Chave da API e Nome do Modelo são obrigatórios.\",\n \"embeddingBaseUrlPlaceholder\": \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n \"embeddingHost\": \"Host\",\n \"embeddingHostTooltip\": \"URL do serviço Ollama. Deixe em branco para usar o host padrão do Ollama.\",\n \"embeddingHostPlaceholder\": \"http://localhost:11434\",\n \"embeddingModelName\": \"Modelo Nome\",\n \"embeddingModelNameTooltip\": \"Nome do modelo de embedding. Obrigatório para todos os backends de embedding.\",\n \"embeddingModelNamePlaceholder\": \"text-embedding-v4\",\n \"embeddingEnableCache\": \"Ativar Embedding Cache\",\n \"embeddingEnableCacheTooltip\": \"Cache embedding results to reduce repeated computation\",\n \"embeddingMaxCacheSize\": \"Max Cache Tamanho\",\n \"embeddingMaxCacheSizeTooltip\": \"Maximum number of entries in the embedding cache\",\n \"embeddingMaxCacheSizeRequired\": \"Max cache Tamanho e obrigatorio\",\n \"embeddingUseDimensions\": \"Use Personalizado Dimensions\",\n \"embeddingUseDimensionsTooltip\": \"Use custom embedding dimensions (only supported by some models)\",\n \"embeddingDimensions\": \"Embedding Dimensions\",\n \"embeddingDimensionsTooltip\": \"Number of embedding dimensions; takes effect when custom dimensions are enabled\",\n \"embeddingDimensionsRequired\": \"Embedding dimensions is Obrigatorio\",\n \"embeddingDimensionsMin\": \"Dimensions must be at least 1\",\n \"embeddingMaxInputLength\": \"Max Input Length\",\n \"embeddingMaxInputLengthTooltip\": \"Maximum input length (tokens) for a single embedding request\",\n \"embeddingMaxInputLengthRequired\": \"Max input length e obrigatorio\",\n \"embeddingMaxBatchSize\": \"Max Batch Tamanho\",\n \"embeddingMaxBatchSizeTooltip\": \"Maximum number of items in a single batch embedding request\",\n \"embeddingMaxBatchSizeRequired\": \"Max batch Tamanho e obrigatorio\",\n \"embeddingRestartWarning\": \"Embedding model config does not support hot reload. Save and restart the service for changes to take effect.\",\n \"toolExecutionLevelTitle\": \"Tool Execution Seguranca\",\n \"toolExecutionLevel\": {\n \"title\": \"Tool Execution Seguranca\",\n \"alertMessage\": \"Configure tool call approval policy to control the security level when agents execute tools\",\n \"strict\": \"Strict Modo\",\n \"strictDesc\": \"All tool calls require approval, highest security level\",\n \"smart\": \"Smart Modo\",\n \"smartDesc\": \"Low-risk tools auto-approved, medium+ risk tools require approval\",\n \"auto\": \"Automatico Modo\",\n \"autoDesc\": \"Only tools explicitly marked for approval require approval (default)\",\n \"off\": \"Desligado Modo\",\n \"offDesc\": \"Disable all tool approvals, all tools execute automatically\"\n },\n \"saveLevelSuccess\": \"Tool execution security level saved\",\n \"saveLevelFailed\": \"Failed to save tool execution security level\",\n \"maxContextLength\": \"Max Context Length\",\n \"maxContextLengthTooltip\": \"Maximum token count for the model context window, determining how much history fits in each request\",\n \"maxContextLengthRequired\": \"Max context length e obrigatorio\",\n \"maxContextLengthMin\": \"Max context length must be at least 1000\",\n \"maxContextLengthPlaceholder\": \"Enter max context length\",\n \"embeddingActivationNote\": \"Embedding é ativado quando os campos obrigatórios do backend selecionado são preenchidos.\",\n \"embeddingEnableHint\": \"Embedding é ativado quando os campos obrigatórios são preenchidos: Nome do Modelo e Chave da API para backends compatíveis com OpenAI ou Gemini; Nome do Modelo para Ollama.\",\n \"rebuildMemoryIndex\": \"Reconstruir índice de memória\",\n \"rebuildMemoryIndexWarning\": \"Esta operação limpa e reconstrói o índice de pesquisa da memória e pode consumir muita CPU e memória. Use-a apenas para reparar um índice danificado ou resultados de pesquisa anormais.\",\n \"rebuildMemoryIndexConfirmTitle\": \"Reconstruir o índice de memória?\",\n \"rebuildMemoryIndexConfirm\": \"O uso de CPU e memória pode aumentar significativamente durante a reconstrução. Esta operação não é recomendada, a menos que o índice esteja danificado ou precise de reparo.\",\n \"rebuildMemoryIndexSuccess\": \"Reconstrução do índice de memória concluída\",\n \"rebuildMemoryIndexFailed\": \"Falha ao reconstruir o índice de memória: {{error}}\",\n \"toolResultCompactRecentGtOld\": \"Recent tool result preview bytes must be greater than or equal to compact preview bytes\",\n \"lightContextTitle\": \"Context Management\",\n \"visualCompactCollapseLabel\": \"Compactação Visual\",\n \"visualCompactDescription\": \"Compacte contextos antigos e longos em páginas visuais para reduzir tokens de entrada. As conversas recentes permanecem em texto.\",\n \"visualCompactEnabled\": \"Ativar Visual Compact\",\n \"visualCompactEffort\": \"Intensidade da compactação\",\n \"visualCompactLow\": \"Baixa\",\n \"visualCompactMedium\": \"Média\",\n \"visualCompactHigh\": \"Alta\",\n \"visualCompactLowDescription\": \"Compacta menos conteúdo e prioriza a legibilidade.\",\n \"visualCompactMediumDescription\": \"Equilibra a economia de tokens com a legibilidade visual.\",\n \"visualCompactHighDescription\": \"Compacta mais contexto elegível e prioriza a economia de tokens.\",\n \"visualCompactQualityNote\": \"Uma compactação maior não melhora necessariamente a qualidade da resposta.\",\n \"visualCompactCapabilityNote\": \"Só funciona com modelos que aceitam entrada de imagem.\",\n \"remeLightMemoryTitle\": \"Long-term Memory\",\n \"dialogPath\": \"Dialog Storage Caminho\",\n \"dialogPathTooltip\": \"Relative path for dialog persistence storage (relative to working_dir)\",\n \"dialogPathPlaceholder\": \"dialog\",\n \"contextCompactCollapseLabel\": \"Context Compaction\",\n \"toolResultPruningCollapseLabel\": \"Tool Result Pruning\",\n \"autoMemorySearchCollapseLabel\": \"Automatico Memory Pesquisar\",\n \"embeddingConfigCollapseLabel\": \"Embedding Modelo Configuracao\",\n \"summarizeWhenCompact\": \"Memory Ligado Compact\",\n \"summarizeWhenCompactTooltip\": \"Automatically generate and save memory during context compaction\",\n \"inboxPushEnabled\": \"Notificações de resultados da memória\",\n \"inboxPushEnabledTooltip\": \"Envia resultados de memória, sonho e recursos automáticos para a caixa de entrada. Desativar não interrompe as tarefas de memória.\",\n \"autoMemoryInterval\": \"Intervalo de Memória Automática\",\n \"autoMemoryIntervalTooltip\": \"Acione a memória automática a cada N consultas do usuário. 0 desativa a memória automática periódica. 1 significa memória automática após cada consulta. Recomendado: 2-5; usar 1 exige atenção ao alto consumo de tokens.\",\n \"autoMemoryIntervalPlaceholder\": \"Digite 0 para desativar a memória automática periódica\",\n \"autoMemoryIntervalRequired\": \"O intervalo de memória automática é obrigatório\",\n \"autoMemoryIntervalMin\": \"O intervalo de memória automática deve ser no mínimo 0\",\n \"contextManagerBackend\": \"Context Manager Backend\",\n \"contextManagerBackendTooltip\": \"Backend type for the context manager. Currently only Light is supported.\",\n \"contextStrategy\": \"Context Strategy\",\n \"contextStrategyTooltip\": \"How the agent manages long context. Scroll (recommended) persists evicted turns to a durable history store and recalls them on demand; Native uses in-place compression. Restart QwenPaw for changes to take effect.\",\n \"contextStrategyScroll\": \"Scroll\",\n \"contextStrategyNative\": \"Native\",\n \"backendRestartWarning\": \"Switching backends does not support hot reload. Save and restart QwenPaw for changes to take effect.\"\n },\n \"tools\": {\n \"title\": \"Integrado Ferramentas\",\n \"description\": \"Manage built-in tools and their enabled status. Disabled tools will not be available to the agent.\",\n \"emptyState\": \"Nao Ferramentas configured\",\n \"loadError\": \"Falhou to load Ferramentas\",\n \"enableSuccess\": \"Tool Ativado successfully\",\n \"disableSuccess\": \"Tool Desativado successfully\",\n \"toggleError\": \"Falha ao toggle tool Status\",\n \"enableAll\": \"Ativar Todos\",\n \"disableAll\": \"Desativar Todos\",\n \"enableAllSuccess\": \"Todos Ferramentas Ativado\",\n \"disableAllSuccess\": \"Todos Ferramentas Desativado\",\n \"allEnabled\": \"All tools are already enabled\",\n \"allDisabled\": \"All tools are already disabled\",\n \"asyncExecution\": \"Async Execution\",\n \"asyncExecutionEnabled\": \"Async execution Ativado\",\n \"asyncExecutionDisabled\": \"Async execution Desativado\",\n \"browserUnifiedMode\": \"Navegador unificado (Beta)\",\n \"browserLegacyMode\": \"Modo de compatibilidade legado\",\n \"browserUnifiedModeButton\": \"Novo (Beta)\",\n \"browserLegacyModeButton\": \"Legado (Compatível)\",\n \"browserUnifiedDescription\": \"Use os novos recursos do navegador unificado.\",\n \"browserLegacyDescription\": \"Use a implementação original do navegador para o comportamento legado.\",\n \"browserRestartPending\": \"O serviço mudará para {{mode}} após reiniciar.\",\n \"configure\": \"Configurar\",\n \"configured\": \"Configurado\",\n \"requiresConfig\": \"Requer configuração\",\n \"configSaved\": \"Configuração salva\",\n \"configSaveError\": \"Falha ao salvar configuração\",\n \"active\": \"ativo(s)\",\n \"available\": \"Disponível\",\n \"noEnabled\": \"Nenhuma ferramenta ativada\",\n \"goEnableBtn\": \"Ativar agora\",\n \"configureAction\": \"Configurar →\",\n \"enableAction\": \"Ativar →\"\n },\n \"modelConfig\": {\n \"promptTitle\": \"LLM Model Required — Select in Chat After Configuration\",\n \"promptMessage\": \"Chat requires an LLM model to function. Please configure a model first, then select it from the model selector at the top of the Chat page.\",\n \"configureButton\": \"Configure Modelo\",\n \"skipButton\": \"Skip\",\n \"chatDisabledTitle\": \"Chat Desativado\",\n \"chatDisabledMessage\": \"Chat functionality is disabled because no model is configured. Please configure a model to enable chat.\",\n \"configureNow\": \"Configure Modelo\",\n \"modelNotConfigured\": \"Please configure a model in Settings before using chat\"\n },\n \"header\": {\n \"changelog\": \"Registro de Alterações\",\n \"docs\": \"Documentação\",\n \"tutorial\": \"Tutorial\",\n \"featureDemos\": \"Demonstração de Funções\",\n \"faq\": \"Perguntas Frequentes\",\n \"github\": \"GitHub\",\n \"resources\": \"Documentação\",\n \"updateAvailable\": \"Atualização Disponível\"\n },\n \"theme\": {\n \"dark\": \"Dark\",\n \"light\": \"Light\",\n \"system\": \"System\"\n },\n \"sidebar\": {\n \"newVersion\": \"New version available: v{{version}}, click to upgrade\",\n \"updateModal\": {\n \"title\": \"Novo Versao available: v{{version}}\",\n \"viewReleases\": \"Visualizar Releases\",\n \"installDesktopUpdate\": \"Atualizar agora\",\n \"desktopInstallHint\": \"QwenPaw Desktop {{version}} está pronto para instalar. O aplicativo será reiniciado após a atualização.\",\n \"checking\": \"Verificando atualizações…\",\n \"checkingHint\": \"Confirmando a versão mais recente\",\n \"downloading\": \"Baixando atualização\",\n \"downloadingTo\": \"Atualizando para a versão {{version}}\",\n \"downloadProgress\": \"{{done}} / {{total}} · {{rate}}\",\n \"installing\": \"Instalando atualização\",\n \"willRestart\": \"O aplicativo será reiniciado automaticamente após a conclusão.\",\n \"stepPrepare\": \"Preparar\",\n \"stepDownloading\": \"Baixar\",\n \"stepInstalling\": \"Instalar\",\n \"failedTitle\": \"Falha na atualização\",\n \"back\": \"Voltar\",\n \"retry\": \"Tentar Novamente\",\n \"errors\": {\n \"network\": \"Não foi possível alcançar o servidor de atualização. Verifique sua internet e tente novamente.\",\n \"signature\": \"Assinatura do arquivo de atualização inválida. Tente baixar novamente.\",\n \"appLocation\": \"O QwenPaw está sendo executado a partir de um local somente leitura do macOS. Mova QwenPaw.app para a pasta Aplicativos, abra-o a partir de lá e tente atualizar novamente.\",\n \"other\": \"Falha na atualização.\"\n },\n \"updateLater\": \"Baixar em segundo plano\",\n \"backgroundDownloading\": \"Baixando em segundo plano…\",\n \"readyToInstall\": \"Atualização pronta\",\n \"readyToInstallHint\": \"A atualização v{{version}} foi baixada. Reinicie para instalar e abrir a nova versão.\",\n \"restartNow\": \"Atualizar agora\",\n \"backgroundFailed\": \"Falha no download em segundo plano\"\n },\n \"settings\": {\n \"language\": \"Idioma\",\n \"theme\": \"Tema\",\n \"mode\": \"Modo\"\n },\n \"simpleMode\": \"Modo simples\",\n \"fullMode\": \"Modo completo\"\n },\n \"chat\": {\n \"modelSelectTooltip\": \"Selecionar Modelo\",\n \"newChatTooltip\": \"Novo Chat\",\n \"searchTooltip\": \"Pesquisar Todos chats\",\n \"chatHistoryTooltip\": \"Chat history\",\n \"wideModeTooltip\": \"Modo amplo\",\n \"normalModeTooltip\": \"Modo normal\",\n \"allChats\": \"Todos Chats\",\n \"createNewChat\": \"Criar Novo Chat\",\n \"pinDrawer\": \"Fixar\",\n \"unpinDrawer\": \"Desafixar\",\n \"statusIdle\": \"Idle\",\n \"statusInProgress\": \"In progress\",\n \"disclaimer\": \"Works for you, grows with you\",\n \"greeting\": \"Hello, how can I help you today?\",\n \"description\": \"I am a helpful assistant that can help you with your questions.\",\n \"prompt1\": \"Let's start a new journey!\",\n \"prompt2\": \"Can you tell me what skills you have?\",\n \"inputPlaceholder\": \"\\\"↑↓\\\" for message navigation · \\\"/\\\" for quick commands (\\\"/approve\\\" or \\\"/deny\\\" during approvals)\",\n \"longTextUploadPrompt\": \"Please read the uploaded prompt file and answer it.\",\n \"emptyOutputError\": \"O assistente retornou uma resposta vazia.\",\n \"commands\": {\n \"clear\": {\n \"description\": \"Limpar the conversation context\"\n },\n \"compact\": {\n \"description\": \"Compact the conversation context; optional instruction supported\"\n },\n \"approve\": {\n \"description\": \"Approve the pending tool action and continue execution\"\n },\n \"deny\": {\n \"description\": \"Deny the pending tool action\"\n },\n \"mission\": {\n \"description\": \"Modo Mission — Decompõe tarefas em sub-agentes com contexto isolado\"\n },\n \"skills\": {\n \"description\": \"List chat-available skills and expose explicit skill commands\"\n },\n \"agents\": {\n \"description\": \"Listar os agentes disponíveis no runtime atual\"\n },\n \"review\": {\n \"description\": \"Revisar com o Codex as alterações não commitadas no workspace\"\n },\n \"status\": {\n \"description\": \"Mostrar a conta, o modelo e o workspace atuais do agente\"\n },\n \"goal\": {\n \"description\": \"Loop Goal — Agente persegue objetivo continuamente, auto-avalia via Rubric\"\n }\n },\n \"attachments\": {\n \"tooltip\": \"Supports documents, images, videos, audio and more formats\",\n \"tooltipImageOnly\": \"Current model supports image upload only\",\n \"tooltipNoMultimodal\": \"No multimodal capability detected for current model, images or videos may not be processed correctly\",\n \"fileSizeLimit\": \"File size limit: {{limit}}MB\",\n \"fileSizeExceeded\": \"File size exceeds {{limit}}MB limit. Current file: {{size}}MB\",\n \"multimodalDisabled\": \"Current model does not support file upload\",\n \"multimodalWarning\": \"No multimodal capability detected for current model, images or videos may not be processed correctly\",\n \"imageOnlyAccept\": \"Current model only supports image files\",\n \"imageOnlyWarning\": \"Current model only supports images, non-image files like videos may not be processed correctly\"\n },\n \"rateLimitHit\": \"O modelo atual está com limite de taxa. Tente mudar para outro modelo gratuito.\",\n \"turnUsagePopover\": {\n \"ariaLabel\": \"Ver uso de tokens e contexto desta rodada\",\n \"turn\": \"Esta rodada\",\n \"turnEstimated\": \"Esta rodada ~\",\n \"tok\": \"tok\",\n \"inOut\": \"in {{inTok}} · out {{outTok}}\",\n \"contextLabel\": \"Janela de contexto:\",\n \"manageContext\": \"Gerenciar contexto\",\n \"compact\": \"Compactar\",\n \"new\": \"Nova conversa\"\n },\n \"rateLimitMessage\": \"Limite de taxa atingido. Mude para outro modelo gratuito:\",\n \"rateLimitSwitched\": \"Mudou para {{model}}\",\n \"speech\": {\n \"startRecording\": \"Iniciar gravação de voz (Ctrl+Shift+M)\",\n \"stopRecording\": \"Parar gravação (Ctrl+Shift+M)\",\n \"transcribing\": \"Transcrevendo...\",\n \"transcriptionDisabled\": \"A transcrição está desativada. Configure um provedor em Configurações > Transcrição de Voz.\",\n \"transcriptionFailed\": \"Falha na transcrição de voz\",\n \"microphoneError\": \"Não foi possível acessar o microfone\",\n \"fileTooLarge\": \"O arquivo de gravação é muito grande ({{size}}MB). Máximo permitido: {{limit}}MB\",\n \"recordingTooLong\": \"A gravação excede o limite de duração ({{limit}}s)\"\n },\n \"search\": {\n \"title\": \"Pesquisar Chat\",\n \"tooltip\": \"Pesquisar Atual Chat\",\n \"placeholder\": \"Pesquisar messages...\",\n \"noResults\": \"Nao results found\",\n \"resultsCount\": \"Found {{count}} result(s)\",\n \"searching\": \"Searching {{progress}}...\",\n \"loading\": \"Carregando...\",\n \"userMessage\": \"Usuario\",\n \"assistantMessage\": \"Assistant\",\n \"titleMatch\": \"Title\"\n },\n \"contextMenu\": {\n \"open\": \"Abrir\",\n \"rename\": \"Rename\",\n \"pin\": \"Pin\",\n \"unpin\": \"Unpin\",\n \"delete\": \"Excluir\"\n },\n \"queue\": {\n \"title\": \"Fila de mensagens\",\n \"paused\": \"Pausado\",\n \"sendFailed\": \"Falha no envio\",\n \"resume\": \"Retomar fila\",\n \"pause\": \"Pausar fila\",\n \"clear\": \"Limpar fila\",\n \"dragSort\": \"Arraste para reordenar\",\n \"edit\": \"Editar\",\n \"retry\": \"Tentar novamente\",\n \"skip\": \"Pular\",\n \"interruptAndSend\": \"Interromper e enviar\",\n \"delete\": \"Excluir\",\n \"file\": \"Arquivo\",\n \"noChatWarning\": \"Envie uma mensagem primeiro para criar uma conversa antes de usar a fila\",\n \"queueFull\": \"Fila cheia (máx. {{max}} itens). Envie ou remova algumas mensagens primeiro\",\n \"otherTabOwner\": \"This tab queues only; sending is handled by another tab\"\n },\n \"group\": {\n \"pinned\": \"Fixados\",\n \"today\": \"Hoje\",\n \"week\": \"Últimos 7 dias\",\n \"month\": \"Últimos 30 dias\",\n \"older\": \"Anteriores\"\n },\n \"sessionPanel\": {\n \"searchConversations\": \"Pesquisar…\",\n \"noResults\": \"Nenhum resultado\",\n \"noConversations\": \"Sem conversas\",\n \"loadingConversations\": \"Carregando…\"\n },\n \"conversationHistory\": \"Histórico de conversas\",\n \"history\": {\n \"empty\": \"Sem histórico de conversas\"\n },\n \"sessionGroup\": {\n \"today\": \"Hoje\",\n \"recent\": \"Últimos 7 dias\",\n \"earlier\": \"Anteriores\"\n }\n },\n \"security\": {\n \"parent\": \"Configuracoes\",\n \"security\": \"Seguranca\",\n \"title\": \"Seguranca\",\n \"description\": \"Manage security features for tools and skills.\",\n \"toolGuardTitle\": \"Tool Guard\",\n \"toolGuardDescription\": \"Configure security scanning for tool calls. Dangerous operations will require your explicit approval before execution.\",\n \"enabled\": \"Ativar Tool Guard\",\n \"enabledTooltip\": \"When enabled, tool calls are scanned for dangerous patterns before execution\",\n \"guardedTools\": \"Guarded Ferramentas\",\n \"guardedToolsTooltip\": \"Tools that require approval when dangerous patterns are detected. Leave empty to use the built-in default set.\",\n \"guardedToolsPlaceholder\": \"Select tools or type custom tool names\",\n \"deniedTools\": \"Denied Ferramentas\",\n \"deniedToolsTooltip\": \"Tools that are always rejected without asking for approval\",\n \"deniedToolsPlaceholder\": \"Select tools to always deny\",\n \"saveSuccess\": \"Tool guard Configuracoes saved\",\n \"saveFailed\": \"Falha ao Salvar tool guard Configuracoes\",\n \"loadFailed\": \"Falha ao load tool guard Configuracoes\",\n \"rules\": {\n \"title\": \"Detection Rules\",\n \"description\": \"Rules define regex patterns that trigger security alerts. Built-in rules can be disabled; custom rules can be added, edited, or deleted.\",\n \"id\": \"Rule ID\",\n \"severity\": \"Severity\",\n \"descriptionCol\": \"Descricao\",\n \"source\": \"Origem\",\n \"actions\": \"Acoes\",\n \"builtin\": \"Integrado\",\n \"custom\": \"Personalizado\",\n \"enable\": \"Ativar\",\n \"disable\": \"Desativar\",\n \"edit\": \"Editar\",\n \"delete\": \"Excluir\",\n \"add\": \"Adicionar Rule\",\n \"addTitle\": \"Adicionar Personalizado Rule\",\n \"editTitle\": \"Editar Personalizado Rule\",\n \"ruleId\": \"Rule ID\",\n \"ruleIdRequired\": \"Rule ID is Obrigatorio\",\n \"duplicateId\": \"A rule with this ID already exists\",\n \"tools\": \"Target Ferramentas\",\n \"toolsPlaceholder\": \"Leave empty to match all tools\",\n \"params\": \"Target Parameters\",\n \"paramsPlaceholder\": \"Leave empty to match all parameters\",\n \"severityLabel\": \"Severity\",\n \"categoryLabel\": \"Category\",\n \"patterns\": \"Regex Patterns\",\n \"patternsRequired\": \"At least one pattern e obrigatorio\",\n \"patternsTooltip\": \"One regex per line. Matched against tool parameter values (case-insensitive).\",\n \"excludePatterns\": \"Exclude Patterns\",\n \"excludePatternsTooltip\": \"One regex per line. If any matches, the rule is skipped for that value.\",\n \"descriptionLabel\": \"Descricao\",\n \"descriptionPlaceholder\": \"What does this rule detect?\",\n \"remediationLabel\": \"Remediation\",\n \"remediationPlaceholder\": \"Suggested action when this rule triggers\",\n \"preview\": \"Visualizar\",\n \"previewTitle\": \"Rule Detalhes\",\n \"actionLabel\": \"Action\",\n \"actionApproval\": \"Require Approval\",\n \"allTools\": \"Todos Ferramentas\",\n \"allParams\": \"Todos parameters\",\n \"descriptions\": {\n \"TOOL_CMD_CONTROL_CHARS\": \"Detects commands with non-printable control characters that may bypass security checks\",\n \"TOOL_CMD_DANGEROUS_MV\": \"Detects 'mv' command that may move or overwrite files unexpectedly\",\n \"TOOL_CMD_DANGEROUS_RM\": \"Detects 'rm' command that may cause data loss\",\n \"TOOL_CMD_DOS_FORK_BOMB\": \"Detects classic Bash fork bombs and mass process termination\",\n \"TOOL_CMD_FIND_DELETE\": \"Detects 'find -delete' command that may cause data loss\",\n \"TOOL_CMD_FS_DESTRUCTION\": \"Detects low-level disk formatting or wiping commands\",\n \"TOOL_CMD_IFS_INJECTION\": \"Detects commands using $IFS variable that may bypass security validation\",\n \"TOOL_CMD_JQ_FILE_FLAGS\": \"Detects jq commands using flags that may read arbitrary files or execute external code\",\n \"TOOL_CMD_JQ_SYSTEM\": \"Detects jq commands with system() function that may execute arbitrary shell commands\",\n \"TOOL_CMD_OBFUSCATED_EXEC\": \"Detects execution of base64 encoded strings passed directly to a shell interpreter\",\n \"TOOL_CMD_PIPE_TO_SHELL\": \"Detects 'curl | bash' patterns used to download and immediately execute remote payloads\",\n \"TOOL_CMD_PRIVILEGE_ESCALATION\": \"Detects privilege escalation attempts using sudo, su, doas, pkexec, or runas\",\n \"TOOL_CMD_PROCESS_KILL\": \"Detects process termination commands that may kill critical processes\",\n \"TOOL_CMD_PROC_ENVIRON\": \"Detects commands accessing /proc/*/environ that may expose sensitive environment variables\",\n \"TOOL_CMD_REVERSE_SHELL\": \"Detects attempts to establish reverse shells or unauthorized network tunnels\",\n \"TOOL_CMD_SERVICE_RESTART\": \"Detects service management commands that can disrupt system services\",\n \"TOOL_CMD_SYSTEM_REBOOT\": \"Detects system reboot or shutdown commands that will terminate the host system\",\n \"TOOL_CMD_SYSTEM_TAMPERING\": \"Detects access to cron jobs, SSH keys, or sudo permissions (including reads and modifications)\",\n \"TOOL_CMD_UNICODE_WHITESPACE\": \"Detects commands with Unicode whitespace characters that may cause parsing issues\",\n \"TOOL_CMD_UNSAFE_PERMISSIONS\": \"Detects global permission downgrades (chmod 777) or setting immutable flags\",\n \"TOOL_CMD_ZSH_DANGEROUS\": \"Detects commands using Zsh-specific builtins that may bypass security checks\"\n },\n \"categories\": {\n \"command_injection\": \"Command Injection\",\n \"code_execution\": \"Code Execution\",\n \"data_exfiltration\": \"Data Exfiltration\",\n \"path_traversal\": \"Caminho Traversal\",\n \"sensitive_file_access\": \"Sensitive File Access\",\n \"network_abuse\": \"Network Abuse\",\n \"credential_exposure\": \"Credential Exposure\",\n \"resource_abuse\": \"Resource Abuse\",\n \"privilege_escalation\": \"Privilege Escalation\",\n \"prompt_injection\": \"Prompt Injection\",\n \"other\": \"Other\"\n }\n },\n \"shellEvasion\": {\n \"title\": \"Shell Evasion Detection\",\n \"description\": \"Configure which shell evasion and obfuscation detection checks are enabled. These checks detect techniques that attempt to hide malicious intent from simpler regex-only detection.\",\n \"checks\": {\n \"command_substitution\": {\n \"name\": \"Command Substitution\",\n \"description\": \"Detects $(), backticks, process substitution, and other command substitution patterns\"\n },\n \"obfuscated_flags\": {\n \"name\": \"Obfuscated Flags\",\n \"description\": \"Detects ANSI-C quoting, locale quoting, and empty-quote flag obfuscation\"\n },\n \"backslash_escaped_whitespace\": {\n \"name\": \"Backslash Escaped Whitespace\",\n \"description\": \"Detects backslash-escaped spaces/tabs that can alter command parsing\"\n },\n \"backslash_escaped_operators\": {\n \"name\": \"Backslash Escaped Operators\",\n \"description\": \"Detects backslash before shell operators (;|&<>) that can hide command structure\"\n },\n \"newlines\": {\n \"name\": \"Hidden Newlines\",\n \"description\": \"Detects newlines and carriage returns that could separate hidden commands\"\n },\n \"comment_quote_desync\": {\n \"name\": \"Comment Quote Desync\",\n \"description\": \"Detects quote characters inside # comments that can desync quote tracking\"\n },\n \"quoted_newline\": {\n \"name\": \"Quoted Newline\",\n \"description\": \"Detects newlines inside quoted strings followed by #-prefixed lines that can hide arguments\"\n }\n }\n },\n \"fileGuard\": {\n \"title\": \"File Guard\",\n \"description\": \"Protect sensitive files and directories from being accessed by agent tools. Paths added here will be blocked across all tool calls.\",\n \"enableLabel\": \"Ativar File Guard\",\n \"path\": \"Caminho\",\n \"actions\": \"Acoes\",\n \"add\": \"Adicionar\",\n \"inputPlaceholder\": \"Enter file or directory path (e.g. ~/.ssh/ or /etc/passwd)\",\n \"empty\": \"No custom sensitive paths configured\",\n \"directory\": \"Directory\",\n \"removeConfirm\": \"Remove this path from the protected list?\",\n \"duplicate\": \"This path is already in the list\",\n \"saveSuccess\": \"File guard Configuracoes saved\",\n \"saveFailed\": \"Falha ao Salvar file guard Configuracoes\",\n \"loadFailed\": \"Falha ao load file guard Configuracoes\",\n \"allowPreviewOutsideWorkspace\": \"Permitir Preview Fora do Workspace no Console\",\n \"allowPreviewOutsideWorkspaceDesc\": \"Permite visualizar arquivos fora do diretório do workspace no console. A proteção de arquivos sensíveis continua ativa.\"\n },\n \"allowNoAuthHosts\": {\n \"title\": \"Allow Nao Auth Hosts\",\n \"warningTitle\": \"Seguranca Aviso\",\n \"warningDescription\": \"IP addresses in this list can access API endpoints without authentication. By default, localhost (127.0.0.1 and ::1) are allowed for CLI access. Only add trusted IP addresses. WARNING: Adding untrusted IPs poses a serious security risk.\",\n \"ipAddress\": \"IP Address\",\n \"actions\": \"Acoes\",\n \"add\": \"Adicionar\",\n \"inputPlaceholder\": \"Enter IP address (e.g., 192.168.1.100 or ::1)\",\n \"empty\": \"Nao IP addresses configured\",\n \"default\": \"Padrao\",\n \"removeConfirm\": \"Remove this IP address from the whitelist?\",\n \"duplicate\": \"This IP address is already in the list\",\n \"invalidIP\": \"Invalid IP address Formato\",\n \"saveSuccess\": \"Allow no auth hosts settings saved\",\n \"saveFailed\": \"Failed to save allow no auth hosts settings\",\n \"loadFailed\": \"Failed to load allow no auth hosts settings\"\n },\n \"skillScanner\": {\n \"title\": \"Skill Scanner\",\n \"description\": \"Automatically scan skills for security threats before enabling or installing. Unsafe skills can be blocked or whitelisted.\",\n \"mode\": \"Scanner Modo\",\n \"modeTooltip\": \"Controls how the scanner handles unsafe skills: block, warn only, or disabled\",\n \"modeBlock\": \"Block\",\n \"modeWarn\": \"Warn Only\",\n \"modeOff\": \"Desligado\",\n \"timeout\": \"Scan Tempo limite (seconds)\",\n \"timeoutTooltip\": \"Maximum time to wait for a scan to complete (5-300 seconds)\",\n \"saveSuccess\": \"Skill scanner Configuracoes saved\",\n \"saveFailed\": \"Falha ao Salvar Skill scanner Configuracoes\",\n \"scanAlerts\": {\n \"title\": \"Scan Alerts\",\n \"empty\": \"Nao Seguranca alerts\",\n \"clearAll\": \"Limpar Todos\",\n \"clearConfirm\": \"Limpar Todos scan alerts?\",\n \"skillName\": \"Skill\",\n \"action\": \"Action\",\n \"actionBlocked\": \"Blocked\",\n \"actionWarned\": \"Warned\",\n \"time\": \"Time\",\n \"actions\": \"Acoes\",\n \"allowSkill\": \"Adicionar to Whitelist\",\n \"remove\": \"Remover\",\n \"viewFindings\": \"Visualizar Detalhes\"\n },\n \"whitelist\": {\n \"title\": \"Whitelist\",\n \"empty\": \"Nao Skills are whitelisted\",\n \"skillName\": \"Skill\",\n \"addedAt\": \"Added At\",\n \"contentHash\": \"Conteudo Hash\",\n \"actions\": \"Acoes\",\n \"remove\": \"Remover\",\n \"removeConfirm\": \"Remove this skill from the whitelist?\",\n \"addSuccess\": \"Skill added to whitelist\",\n \"removeSuccess\": \"Skill removed from whitelist\",\n \"addFailed\": \"Falha ao Adicionar Skill to whitelist\",\n \"removeFailed\": \"Falha ao Remover Skill from whitelist\",\n \"removeWillDisable\": \"The skill will also be disabled after removal.\",\n \"removeAndDisabled\": \"Skill removed from whitelist and disabled\"\n },\n \"scanError\": {\n \"title\": \"Seguranca Issues Detected\",\n \"description\": \"The following security issues were found:\",\n \"warnDescription\": \"Security issues were found but the skill was still enabled (warn mode):\",\n \"goToWhitelist\": \"Go to Whitelist\",\n \"moreFindings\": \"{{count}} more issues not shown (see Security → Skill Scanner for the full list).\"\n }\n }\n },\n \"login\": {\n \"title\": \"Login to QwenPaw\",\n \"registerTitle\": \"Criar Account\",\n \"firstUserHint\": \"Create your admin account to get started\",\n \"usernamePlaceholder\": \"Username\",\n \"passwordPlaceholder\": \"Password\",\n \"usernameRequired\": \"Please enter your username\",\n \"passwordRequired\": \"Please enter your password\",\n \"submit\": \"Login\",\n \"register\": \"Register\",\n \"failed\": \"Login failed, please check your credentials\",\n \"registerSuccess\": \"Registration successful\",\n \"registerFailed\": \"Registration Falhou\",\n \"authNotEnabled\": \"Autenticacao is not Ativado\",\n \"logout\": \"Logout\",\n \"logoutSuccess\": \"Logged out successfully\",\n \"sessionExpired\": \"Session expired, please login again\",\n \"unauthorized\": \"Unauthorized, please login\"\n },\n \"account\": {\n \"title\": \"Account\",\n \"currentPassword\": \"Atual Password\",\n \"currentPasswordRequired\": \"Digite your Atual password\",\n \"newUsername\": \"Novo Username\",\n \"newUsernamePlaceholder\": \"Leave blank to keep current\",\n \"newPassword\": \"Novo Password\",\n \"newPasswordPlaceholder\": \"Leave blank to keep current\",\n \"confirmPassword\": \"Confirmar Novo Password\",\n \"confirmPasswordPlaceholder\": \"Re-enter Novo password\",\n \"passwordMismatch\": \"Passwords do not match\",\n \"save\": \"Salvar Changes\",\n \"updateSuccess\": \"Account Atualizado successfully\",\n \"updateFailed\": \"Falhou to Atualizar account\",\n \"wrongPassword\": \"Atual password is incorrect\",\n \"nothingToUpdate\": \"Please enter a new username or password\",\n \"passwordEmpty\": \"Password cannot be blank\",\n \"usernameEmpty\": \"Username cannot be blank\"\n },\n \"approval\": {\n \"title\": \"Seguranca Approval Obrigatorio\",\n \"tool\": \"Tool\",\n \"severity\": \"Severity\",\n \"findings\": \"Findings\",\n \"parameters\": \"Parameters\",\n \"approve\": \"Approve\",\n \"deny\": \"Deny\",\n \"source\": \"Origem\",\n \"subSession\": \"Sub-Agent\",\n \"approved\": \"Tool call approved\",\n \"denied\": \"Tool call denied\",\n \"approveFailed\": \"Falhou ao aprovar\",\n \"denyFailed\": \"Falhou ao negar\",\n \"approvePattern\": \"Sempre Permitir\",\n \"strictModeHint\": \"Sempre permitir não está disponível no modo estrito\",\n \"approveExact\": \"Apenas Desta Vez\",\n \"approvalScope\": \"Escopo de aprovação\",\n \"details\": \"Detalhes\",\n \"timeoutAutoDenied\": \"Tempo esgotado, negado automaticamente\",\n \"acknowledge\": \"Entendi\",\n \"builtinSource\": \"Integrado\",\n \"ownerAgent\": \"Agente Proprietário\",\n \"executingAgent\": \"Agente Executando\"\n },\n \"pluginManager\": {\n \"title\": \"Gerenciador de Plugins\",\n \"description\": \"Instalar, gerenciar e remover plugins sem reiniciar o serviço.\",\n \"installed\": \"Plugins Instalados\",\n \"noPlugins\": \"Nenhum plugin instalado ainda\",\n \"installBtn\": \"Instalar Plugin\",\n \"installTitle\": \"Instalar Plugin\",\n \"dropPrimary\": \"Solte a pasta ou arquivo ZIP aqui\",\n \"dropSecondary\": \"ou clique para selecionar um arquivo ZIP\",\n \"orFromUrl\": \"ou a partir de URL\",\n \"installFromUrl\": \"Instalar a partir de URL\",\n \"urlPlaceholder\": \"https://example.com/plugin.zip\",\n \"zipOnly\": \"Apenas arquivos ZIP podem ser soltos como arquivos — arraste uma pasta para instalação de diretório\",\n \"dropFailed\": \"Falha ao ler a pasta solta\",\n \"restartHint\": \"Alguns plugins (por exemplo, hooks, monkey-patches) podem exigir uma reinicialização do aplicativo para entrar em vigor ou serem completamente removidos.\",\n \"installing\": \"Instalando...\",\n \"installSuccess\": \"Plugin instalado com sucesso\",\n \"installFailed\": \"Falha na instalação do plugin\",\n \"uninstall\": \"Desinstalar\",\n \"uninstallConfirm\": \"Desinstalar plugin \\\"{{name}}\\\"? Esta ação não pode ser desfeita. Alguns plugins podem exigir uma reinicialização do aplicativo para serem completamente removidos.\",\n \"uninstalling\": \"Desinstalando...\",\n \"uninstallSuccess\": \"Plugin desinstalado com sucesso\",\n \"uninstallFailed\": \"Falha na desinstalação do plugin\",\n \"statusLoaded\": \"Em execução\",\n \"statusUnloaded\": \"Não carregado\",\n \"type\": \"Tipo\",\n \"version\": \"Versão\",\n \"author\": \"Autor\",\n \"unknown\": \"Desconhecido\",\n \"loadFailed\": \"Falha ao carregar lista de plugins\",\n \"confirmTitle\": \"Confirmar Desinstalação\",\n \"officialTitle\": \"Plugins Oficiais\",\n \"officialDesc\": \"Instale plugins publicados a partir do CDN de downloads.\",\n \"catalogRefresh\": \"Atualizar\",\n \"catalogEmpty\": \"Nenhum plugin oficial disponível (o catálogo CDN pode estar vazio)\",\n \"catalogLoadFailed\": \"Falha ao carregar o catálogo de plugins oficiais\",\n \"catalogInstall\": \"Instalar\",\n \"catalogReinstall\": \"Reinstalar\",\n \"catalogUpgrade\": \"Atualização disponível\",\n \"catalogUpgradeBtn\": \"Atualizar\",\n \"catalogInstalled\": \"Instalado\",\n \"catalogSha256\": \"SHA256\",\n \"kindBundle\": \"Pacote\",\n \"kindTool\": \"Ferramenta\",\n \"filterByName\": \"Filtrar por nome\",\n \"filterByKind\": \"Filtrar por tipo\",\n \"marketTitle\": \"Plugin Market\",\n \"marketEmpty\": \"No plugins found in market\",\n \"marketLoadFailed\": \"Failed to load plugin market\",\n \"marketUnavailable\": \"Plugin Market is currently under maintenance, please try again later\",\n \"marketAll\": \"All\",\n \"marketSearch\": \"Search plugins...\",\n \"marketCategory\": \"Filter by category\",\n \"marketSearchResult\": \"Found {{count}} plugins matching \\\"{{keyword}}\\\"\",\n \"marketDetails\": \"Details\",\n \"marketDownloads\": \"Downloads\",\n \"marketDeveloper\": \"Developer\",\n \"marketSortLabel\": \"Sort plugins\",\n \"marketSortDownloads\": \"Most downloaded\",\n \"marketSortUpdated\": \"Recently updated\",\n \"marketSortFavorites\": \"Most favorited\"\n },\n \"inbox\": {\n \"title\": \"Colheita diária de IA\",\n \"tabApprovals\": \"Aprovações\",\n \"tabPushMessages\": \"Mensagens push\",\n \"tabHarvests\": \"Colheita de IA\",\n \"summaryApprovals\": \"Aprovações\",\n \"summaryPushUnread\": \"Push não lidas\",\n \"summaryHarvests\": \"Colheitas\",\n \"from\": \"De:\",\n \"requestedBy\": \"Por\",\n \"markRead\": \"Marcar como lida\",\n \"markAllRead\": \"Marcar todas como lidas\",\n \"markAllReadNoUnread\": \"Nenhuma mensagem não lida\",\n \"markAllReadSuccess\": \"{{count}} mensagens marcadas como lidas\",\n \"selectAllCurrentPage\": \"Selecionar página atual\",\n \"selectedItems\": \"{{count}} selecionado(s)\",\n \"batchDeleteButton\": \"Exclusão em lote\",\n \"batchOperation\": \"Operação em lote\",\n \"exitBatch\": \"Sair do lote\",\n \"batchDeleteConfirm\": \"Excluir {{count}} mensagens push selecionadas?\",\n \"batchDeleteSuccess\": \"{{count}} mensagens push excluídas\",\n \"pushCronHeader\": \"Tarefa Cron: {{name}}\",\n \"viewDetails\": \"Ver detalhes\",\n \"deleteMessageConfirm\": \"Excluir esta mensagem push?\",\n \"viewAll\": \"Ver todas\",\n \"read\": \"Lida\",\n \"approve\": \"Aprovar\",\n \"reject\": \"Rejeitar\",\n \"ready\": \"Pronto\",\n \"harvestNow\": \"Colher agora\",\n \"createHarvest\": \"Criar colheita\",\n \"createFirstHarvest\": \"Crie sua primeira colheita\",\n \"createSuccess\": \"Colheita criada com sucesso\",\n \"emptyApprovals\": \"Nenhuma aprovação pendente\",\n \"emptyPush\": \"Nenhuma mensagem push\",\n \"wobbleEnable\": \"Ativar notificação de aprovação por vibração\",\n \"wobbleDisable\": \"Desativar notificação de aprovação por vibração\",\n \"emptyHarvests\": \"Ainda não há colheitas\",\n \"statusReadyToHarvest\": \"Pronto para colher\",\n \"statusGrowing\": \"Em crescimento\",\n \"harvestedTimes\": \"Colhido {{count}} vez(es)\",\n \"harvestSuccessRate\": \"Taxa de sucesso {{rate}}%\",\n \"messageDetailComingSoon\": \"Visualização de detalhes da mensagem — em breve!\",\n \"harvestSettingsComingSoon\": \"Configurações de colheita — em breve!\",\n \"messageDetailTitle\": \"Detalhe da execução\",\n \"detailCronTitle\": \"Tarefa Cron: {{name}}\",\n \"detailHeartbeatTitle\": \"Batimento\",\n \"messageNotFound\": \"Mensagem não encontrada\",\n \"detailTaskName\": \"Nome da tarefa\",\n \"detailTaskId\": \"ID da tarefa\",\n \"detailStatus\": \"Estado\",\n \"detailSeverity\": \"Gravidade\",\n \"detailAgent\": \"Agente\",\n \"detailSource\": \"Origem\",\n \"detailSourceCronJob\": \"Tarefa Cron\",\n \"detailSourceHeartbeat\": \"Batimento\",\n \"detailTrigger\": \"Disparo\",\n \"detailDuration\": \"Duração\",\n \"detailExecutedAt\": \"Executado em\",\n \"detailReceivedAt\": \"Recebido em\",\n \"detailRunId\": \"ID da execução\",\n \"detailContent\": \"Saída\",\n \"detailPayload\": \"Dados\",\n \"detailExecutionTrace\": \"Rastro de execução\",\n \"detailTraceEmpty\": \"Nenhum rastro de execução disponível\",\n \"filterByAgent\": \"Filtrar por agente\",\n \"filterBySourceType\": \"Filtrar por origem\",\n \"sourceTypeCron\": \"Cron\",\n \"sourceTypeHeartbeat\": \"Heartbeat\",\n \"sourceTypeMemory\": \"Memory\"\n },\n \"codingMode\": {\n \"title\": \"Modo de Codificação\",\n \"experimental\": \"Funcionalidade Experimental\",\n \"experimentalDesc\": \"O Modo de Codificação transforma o QwenPaw em uma IDE Web leve com editor de código, árvore de arquivos, integração Git e assistência de codificação por IA.\",\n \"experimentalNote\": \"Esta funcionalidade é experimental e ainda está em desenvolvimento ativo. Algumas funcionalidades podem ser limitadas ou mudar em atualizações futuras.\",\n \"confirmBtn\": \"Ativar Modo de Codificação\",\n \"enterTooltip\": \"Entrar no Modo de Codificação\",\n \"exitTooltip\": \"Sair do Modo de Codificação\",\n \"btnCode\": \"Código\",\n \"btnChat\": \"Chat\",\n \"selectProject\": \"Selecionar Projeto de Código\",\n \"selectProjectDesc\": \"Escolha um projeto para trabalhar ou use o workspace padrão\",\n \"tabWorkspace\": \"Workspace Padrão\",\n \"tabClone\": \"Clonar Repositório\",\n \"tabLocal\": \"Importar Projeto Local\",\n \"tabNew\": \"Novo Projeto\",\n \"workspaceDesc\": \"Usar o diretório de workspace do agente atual\",\n \"cloneUrl\": \"URL do Repositório Git\",\n \"cloneUrlPlaceholder\": \"https://github.com/user/repo.git\",\n \"cloneName\": \"Nome do Projeto (opcional)\",\n \"cloneNamePlaceholder\": \"Deixe vazio para inferir da URL\",\n \"cloneBtn\": \"Iniciar Clone\",\n \"cloning\": \"Clonando...\",\n \"localPath\": \"Caminho do Diretório Local\",\n \"localPathPlaceholder\": \"/Users/seunome/meu-projeto\",\n \"localPathHint\": \"Insira o caminho absoluto do seu projeto local\",\n \"openBtn\": \"Importar\",\n \"newName\": \"Nome do Projeto\",\n \"newNamePlaceholder\": \"meu-projeto\",\n \"createBtn\": \"Criar Projeto\",\n \"switchProject\": \"Trocar Projeto\",\n \"currentProject\": \"Projeto Atual\",\n \"resetToWorkspace\": \"Redefinir para Workspace Padrão\",\n \"dropPrimary\": \"Arraste uma pasta aqui ou clique para navegar\",\n \"dropSecondary\": \"Arraste do Finder ou cole um caminho abaixo\",\n \"pathHint\": \"Insira o caminho absoluto completo (começando com / ou ~)\",\n \"importing\": \"Importando...\",\n \"dropFailed\": \"Falha ao ler a pasta\",\n \"workingDir\": \"Diretório de trabalho\",\n \"importCopyNote\": \"A pasta será copiada para coding_projects/. Os arquivos originais não serão modificados.\",\n \"importCopyDesc\": \"A importacao cria uma copia da pasta. O Agent trabalha na copia e seus arquivos originais nao sao alterados. Para trabalhar diretamente no original, use \\\"Abrir diretorio\\\".\",\n \"tabOpenDir\": \"Abrir diretório\",\n \"openDirDesc\": \"Abrir um diretório local existente — nenhum arquivo será copiado\",\n \"openDirBtn\": \"Abrir este diretório\",\n \"openDirLoading\": \"Carregando...\",\n \"openDirEmpty\": \"Sem subdiretórios\",\n \"openDirHome\": \"Início\",\n \"openDirRefresh\": \"Atualizar\",\n \"openDirHiddenFolders\": \"Pastas Ocultas\"\n },\n \"market\": {\n \"searchPlaceholder\": \"Buscar habilidades em várias plataformas\",\n \"categoryAll\": \"Todos\",\n \"categoryPlaceholder\": \"Buscar categoria\",\n \"searchResult\": \"{{count}} habilidades encontradas para \\\"{{keyword}}\\\"\",\n \"providerUnavailable\": \"Provedor indisponível\",\n \"noResults\": \"Nenhum resultado\",\n \"startTyping\": \"Digite uma consulta acima para buscar no mercado\",\n \"browseHint\": \"Escolha uma categoria ou digite uma palavra-chave para navegar pelos skills em {{providers}}\",\n \"noDescription\": \"Nenhuma descrição fornecida\",\n \"browseMarket\": \"Explorar Mercado\",\n \"installQueue\": \"Fila de instalação\",\n \"clearCompleted\": \"Limpar\",\n \"retry\": \"Tentar novamente\",\n \"status\": {\n \"queued\": \"Na fila\",\n \"installing\": \"Instalando\",\n \"completed\": \"Concluído\",\n \"failed\": \"Falhou\",\n \"cancelled\": \"Cancelado\"\n },\n \"loadMore\": \"Carregar mais\",\n \"noMoreResults\": \"Sem mais resultados\",\n \"detail\": {\n \"title\": \"Detalhes da habilidade\",\n \"version\": \"Versão\",\n \"author\": \"Autor\",\n \"sourceUrl\": \"URL de instalação\",\n \"slug\": \"Identificador\",\n \"missing\": \"—\"\n },\n \"stats\": {\n \"downloads\": \"Downloads\",\n \"stars\": \"Estrelas\",\n \"installs\": \"Instalações\",\n \"likes\": \"Curtidas\",\n \"views\": \"Visualizações\",\n \"category\": \"Categoria\",\n \"updatedAt\": \"Atualizado em\"\n },\n \"queueMsg\": {\n \"timedOut\": \"A instalação expirou\",\n \"failedPrefix\": \"Falhou: {{msg}}\"\n }\n },\n \"startup\": {\n \"starting\": \"Iniciando backend...\",\n \"checking\": \"Conectando ao backend...\",\n \"error\": \"Falha ao iniciar o backend.\",\n \"errorHint\": \"Nao foi possivel iniciar o processo do backend. Verifique os logs do aplicativo para detalhes.\",\n \"errorDetails\": \"Mostrar detalhes do erro\",\n \"timeout\": \"O backend nao iniciou em {{seconds}} segundos.\",\n \"timeoutHint\": \"Falha ao iniciar o backend. Tente novamente ou verifique os logs do aplicativo.\",\n \"retry\": \"Tentar novamente\"\n },\n \"preview\": {\n \"error\": {\n \"SENSITIVE_FILE_BLOCKED\": \"Este arquivo está bloqueado pelo File Guard (proteção de arquivos sensíveis).\",\n \"OUTSIDE_WORKSPACE\": \"Este arquivo está fora do escopo do workspace. Ative \\\"Permitir Preview Fora do Workspace no Console\\\" em Configurações → Segurança → File Guard.\",\n \"FORBIDDEN\": \"Acesso negado: permissões insuficientes para visualizar este arquivo.\",\n \"NOT_FOUND\": \"Arquivo não encontrado. Pode ter sido movido ou excluído. Se foi usado um caminho relativo, tente usar um caminho absoluto.\",\n \"LOAD_FAILED\": \"Falha ao carregar a visualização do arquivo.\",\n \"LOAD_FAILED_DETAIL\": \"Falha ao carregar a visualização do arquivo: {{detail}}\"\n }\n },\n \"tool\": {\n \"readFile\": \"Ler {{file}}\",\n \"readFileDefault\": \"Ler arquivo\",\n \"writeFile\": \"Criar {{file}}\",\n \"writeFileDefault\": \"Criar arquivo\",\n \"editFile\": \"Editar {{file}}\",\n \"editFileDefault\": \"Editar arquivo\",\n \"appendFile\": \"Adicionar em {{file}}\",\n \"appendFileDefault\": \"Adicionar ao arquivo\",\n \"grepSearch\": \"Grep {{pattern}}\",\n \"grepSearchDefault\": \"Busca grep\",\n \"globSearch\": \"Glob {{pattern}}\",\n \"globSearchDefault\": \"Busca glob\",\n \"viewImage\": \"Ver imagem {{file}}\",\n \"viewImageDefault\": \"Ver imagem\",\n \"viewVideo\": \"Ver vídeo {{file}}\",\n \"viewVideoDefault\": \"Ver vídeo\",\n \"desktopScreenshot\": \"Captura de tela do desktop\",\n \"sendFile\": \"Enviar {{file}}\",\n \"sendFileDefault\": \"Enviar arquivo\",\n \"browserUse\": \"Navegar {{detail}}\",\n \"browserNavigate\": \"Abrir {{url}}\",\n \"browserNavigateDefault\": \"Abrir página\",\n \"browserClick\": \"Clicar\",\n \"browserType\": \"Digitar\",\n \"browserSnapshot\": \"Tirar snapshot\",\n \"browserScroll\": \"Rolar página\",\n \"getCurrentTime\": \"Obter hora atual\",\n \"setTimezone\": \"Definir {{timezone}}\",\n \"getTokenUsage\": \"Consultar uso de tokens\",\n \"memorySearch\": \"Buscar na memória {{query}}\",\n \"memorySearchDefault\": \"Buscar na memória\",\n \"listAgents\": \"Listar agentes\",\n \"chatWithAgent\": \"Conversar com {{agent}}\",\n \"chatWithAgentDefault\": \"Conversar com agente\",\n \"submitToAgent\": \"Enviar para {{agent}}{{task}}\",\n \"submitToAgentDefault\": \"Enviar tarefa\",\n \"checkAgentTask\": \"Verificar {{agent}} #{{taskId}}\",\n \"checkAgentTaskAgent\": \"Verificar {{agent}}\",\n \"checkAgentTaskDefault\": \"Verificar tarefa\",\n \"delegateExternalAgent\": \"Delegar {{runner}}\",\n \"delegateExternalAgentDefault\": \"Delegar agente externo\",\n \"materializeSkill\": \"Criar {{skill}}\",\n \"materializeSkillDefault\": \"Criar habilidade\",\n \"shell\": \"Executar {{command}}\",\n \"shellDefault\": \"Executar comando\",\n \"formatTable\": {\n \"file\": \"Arquivo\",\n \"lineNumber\": \"Linha\",\n \"score\": \"Pontuação\",\n \"summary\": \"Resumo\",\n \"name\": \"Nome\",\n \"id\": \"ID\",\n \"description\": \"Descrição\",\n \"status\": \"Status\"\n },\n \"loading\": \"Carregando...\",\n \"inputProgress\": \"{{count}} caracteres recebidos\",\n \"rawInputPreview\": \"Entrada bruta em tempo real (últimos 4.096 caracteres)\",\n \"largeOutputOmitted\": \"{{count}} linhas omitidas — copie a saída disponível ou siga as instruções de recuperação\",\n \"largeOutputTruncated\": \"Saída truncada para exibição — copie a saída disponível ou siga as instruções de recuperação\",\n \"browserAction\": {\n \"startHeaded\": \"Iniciar navegador (com interface)\",\n \"start\": \"Iniciar navegador\",\n \"stop\": \"Parar navegador\",\n \"open\": \"Abrir {{url}}\",\n \"openDefault\": \"Abrir página\",\n \"navigate\": \"Navegar para {{url}}\",\n \"navigateDefault\": \"Navegar\",\n \"navigateBack\": \"Voltar\",\n \"click\": \"Clicar em {{selector}}\",\n \"clickDefault\": \"Clicar no elemento\",\n \"type\": \"Digitar {{text}}\",\n \"typeDefault\": \"Digitar texto\",\n \"snapshot\": \"Obter snapshot\",\n \"screenshot\": \"Captura de tela {{path}}\",\n \"screenshotDefault\": \"Captura de tela\",\n \"eval\": \"Avaliar {{code}}\",\n \"evalDefault\": \"Avaliar código\",\n \"runCode\": \"Executar {{code}}\",\n \"runCodeDefault\": \"Executar código\",\n \"closePage\": \"Fechar página\",\n \"tabs\": \"Abas {{action}}\",\n \"tabsDefault\": \"Gerenciar abas\",\n \"fillForm\": \"Preencher formulário\",\n \"fileUpload\": \"Fazer upload de {{filename}}\",\n \"fileUploadDefault\": \"Fazer upload de arquivo\",\n \"fileDownload\": \"Baixar {{target}}\",\n \"fileDownloadDefault\": \"Baixar arquivo\",\n \"pressKey\": \"Pressionar {{key}}\",\n \"pressKeyDefault\": \"Pressionar tecla\",\n \"hover\": \"Passar o mouse sobre {{selector}}\",\n \"hoverDefault\": \"Passar o mouse sobre elemento\",\n \"drag\": \"Arrastar\",\n \"selectOption\": \"Selecionar opção\",\n \"waitFor\": \"Aguardar {{target}}\",\n \"waitForDefault\": \"Aguardar elemento\",\n \"resize\": \"Redimensionar {{w}}x{{h}}\",\n \"resizeDefault\": \"Redimensionar janela\",\n \"pdf\": \"Exportar PDF {{path}}\",\n \"pdfDefault\": \"Exportar PDF\",\n \"install\": \"Instalar\",\n \"batch\": \"Lote\"\n },\n \"lineBadge\": {\n \"lines\": \"{{count}} linhas\",\n \"addLines\": \"+{{count}} linhas\",\n \"delLines\": \"-{{count}} linhas\",\n \"matches\": \"{{count}} correspondências\",\n \"files\": \"{{count}} arquivos\"\n },\n \"inlineResult\": {\n \"taskId\": \"Tarefa #{{id}}\",\n \"submitted\": \"Enviado\"\n },\n \"execute\": \"Executar {{tool}}\",\n \"executeDefault\": \"Executar ferramenta\"\n },\n \"loop\": {\n \"gotoSettings\": \"Configurações de Loop do Agente\",\n \"selectorTitle\": \"Modo de Loop\",\n \"selectorHint\": \"Escolha como o agente deve continuar\",\n \"selectorAria\": \"Selecionar modo de Loop\",\n \"builtInModes\": \"Integrados\",\n \"customModes\": \"Personalizados e plugins\",\n \"starting\": \"Iniciando\",\n \"running\": \"Em execução\",\n \"awaiting_user\": \"Aguardando sua resposta\",\n \"activePersistentDescription\": \"Este modo permanece ativo na conversa até terminar ou a conversa ser reiniciada.\",\n \"activeCustomDescription\": \"Este modo personalizado permanece ativo até usar /mode off, /clear ou /new.\",\n \"loadError\": \"Não foi possível carregar alguns modos de Loop.\",\n \"retry\": \"Tentar novamente\",\n \"modes\": {\n \"default\": {\n \"name\": \"Padrão\",\n \"description\": \"O Loop de agente controlado padrão.\"\n },\n \"goal\": {\n \"name\": \"Objetivo\",\n \"description\": \"Trabalhe continuamente em um objetivo concreto e verificável.\"\n },\n \"mission\": {\n \"name\": \"Missão\",\n \"description\": \"Execute uma tarefa estruturada e persistente em várias etapas.\"\n }\n }\n }\n}\n"}}, "console/src/locales/en.json": {"db8fb6391097791b08f63623ba4cd88421ec5cdb": {"t": 1785846849.71475, "n": 188244, "h": "12d7c11f14405820", "c": "{\n \"common\": {\n \"save\": \"Save\",\n \"reset\": \"Reset\",\n \"cancel\": \"Cancel\",\n \"confirm\": \"Confirm\",\n \"delete\": \"Delete\",\n \"edit\": \"Edit\",\n \"create\": \"Create\",\n \"upload\": \"Upload\",\n \"download\": \"Download\",\n \"refresh\": \"Refresh\",\n \"retry\": \"Retry\",\n \"search\": \"Search\",\n \"enable\": \"Enable\",\n \"disable\": \"Disable\",\n \"enabled\": \"Enabled\",\n \"disabled\": \"Disabled\",\n \"preview\": \"Preview\",\n \"view\": \"View\",\n \"content\": \"Content\",\n \"loading\": \"Loading...\",\n \"saving\": \"Saving...\",\n \"copy\": \"Copy\",\n \"copied\": \"Copied to clipboard\",\n \"copyFailed\": \"Failed to copy to clipboard\",\n \"contentPlaceholder\": \"Enter content...\",\n \"help\": \"Help\",\n \"close\": \"Close\",\n \"back\": \"Back\",\n \"actions\": \"Actions\",\n \"total\": \"Total {{count}}\",\n \"clear\": \"Clear\",\n \"paste\": \"Paste\",\n \"yes\": \"Yes\",\n \"no\": \"No\"\n },\n \"desktop\": {\n \"closeWindow\": {\n \"title\": \"Close Window\",\n \"description\": \"What would you like to do when closing the window? Quitting the app stops all running tasks and scheduled jobs.\",\n \"remember\": \"Remember my choice\",\n \"showWindow\": \"Show Window\",\n \"minimizeToTray\": \"Minimize to Tray\",\n \"quitApp\": \"Quit App\",\n \"preference\": \"Close Window\",\n \"askEveryTime\": \"Ask every time\"\n }\n },\n \"chunkError\": {\n \"title\": \"Failed to load page\",\n \"subTitle\": \"This may be caused by a network issue or an application update.\",\n \"genericTitle\": \"Something went wrong\",\n \"genericSubTitle\": \"An unexpected error occurred while rendering this page.\",\n \"reload\": \"Reload\"\n },\n \"appCenter\": {\n \"backToList\": \"Back\",\n \"subtitle\": \"Manage installed apps, or expand your workspace from official and community channels.\",\n \"myApps\": \"My Apps\",\n \"officialApps\": \"Official Apps\",\n \"appMarket\": \"App Market\",\n \"browseOfficialApps\": \"Browse Official Apps\",\n \"browseMarket\": \"Browse App Market\",\n \"officialAppsEmpty\": \"No official apps yet\",\n \"searchOfficial\": \"Search official apps...\",\n \"installedCount\": \"{{count}} apps\",\n \"clearFilters\": \"Clear filters\",\n \"moreActions\": \"More actions\",\n \"noDescription\": \"No description\",\n \"search\": \"Search apps...\",\n \"allCategories\": \"All\",\n \"noApps\": \"No apps installed yet\",\n \"noResults\": \"No apps match your search\",\n \"loadFailed\": \"Failed to load apps. Please retry.\",\n \"appNotLoaded\": \"This app is not loaded yet. Open it once from the sidebar, then retry.\",\n \"uninstall\": \"Uninstall\",\n \"uninstallConfirmTitle\": \"Uninstall app?\",\n \"uninstallConfirmContent\": \"This will delete the app directory of \\\"{{name}}\\\". This cannot be undone.\",\n \"uninstallSuccess\": \"App uninstalled\",\n \"uninstallFailed\": \"Uninstall failed\",\n \"installing\": \"Installing\",\n \"installSuccess\": \"Installed\",\n \"installFailed\": \"Install failed\",\n \"searchMarket\": \"Search app market...\",\n \"marketEmpty\": \"No apps found\",\n \"install\": \"Install\",\n \"details\": \"Details\",\n \"featured\": \"Featured\",\n \"refreshApp\": \"Refresh App\",\n \"appRefreshed\": \"App refreshed\",\n \"aboutApp\": \"About App\",\n \"exitApp\": \"Exit App\",\n \"moreOptions\": \"More Options\",\n \"backToListHint\": \"Back to app list (ESC)\",\n \"version\": \"Version\",\n \"id\": \"ID\",\n \"category\": \"Category\",\n \"description\": \"Description\"\n },\n \"harnesses\": {\n \"connected\": \"Connected\",\n \"disconnected\": \"Disconnected\",\n \"notConnected\": \"Not connected\",\n \"comingSoon\": \"Coming soon\",\n \"connect\": \"Connect with ChatGPT\",\n \"disconnect\": \"Disconnect\",\n \"apiKeyAuthenticated\": \"Authenticated with API key\",\n \"chatGptAuthenticated\": \"Connected with ChatGPT\",\n \"cliAuthenticated\": \"{{type}} CLI authenticated\"\n },\n \"nav\": {\n \"chat\": \"Chat\",\n \"control\": \"Control\",\n \"channels\": \"Channels\",\n \"sessions\": \"Sessions\",\n \"inbox\": \"Inbox\",\n \"apps\": \"Apps\",\n \"cronJobs\": \"Cron Jobs\",\n \"heartbeat\": \"Heartbeat\",\n \"agent\": \"Workspace\",\n \"workspace\": \"Files\",\n \"skills\": \"Skills\",\n \"skillPool\": \"Skill Pool\",\n \"market\": \"Skill Market\",\n \"tools\": \"Tools\",\n \"mcp\": \"MCP\",\n \"acp\": \"ACP\",\n \"agentConfig\": \"Configuration\",\n \"agents\": \"Agent Management\",\n \"settings\": \"Settings\",\n \"models\": \"Models\",\n \"environments\": \"Environments\",\n \"offloadPolicy\": \"Tool Offload\",\n \"security\": \"Security\",\n \"tokenUsage\": \"Token Usage\",\n \"agentStats\": \"Agent Statistics\",\n \"voiceTranscription\": \"Voice Transcription\",\n \"debug\": \"Debug\",\n \"plugins\": \"Plugins\",\n \"backups\": \"Backups\",\n \"pluginManager\": \"Plugin Manager\"\n },\n \"sidebar\": {\n \"newVersion\": \"New version available: v{{version}}, click to upgrade\",\n \"updateModal\": {\n \"title\": \"New version available: v{{version}}\",\n \"viewReleases\": \"View Releases\",\n \"installDesktopUpdate\": \"Update Now\",\n \"desktopInstallHint\": \"QwenPaw Desktop {{version}} is ready to install. The app will restart after the update.\",\n \"checking\": \"Checking for updates…\",\n \"checkingHint\": \"Confirming the latest release\",\n \"downloading\": \"Downloading update\",\n \"downloadingTo\": \"Updating to version {{version}}\",\n \"downloadProgress\": \"{{done}} / {{total}} · {{rate}}\",\n \"installing\": \"Installing update\",\n \"willRestart\": \"The app will restart automatically when done.\",\n \"stepPrepare\": \"Prepare\",\n \"stepDownloading\": \"Download\",\n \"stepInstalling\": \"Install\",\n \"failedTitle\": \"Update failed\",\n \"back\": \"Back\",\n \"retry\": \"Retry\",\n \"errors\": {\n \"network\": \"Couldn't reach the update server. Check your internet and try again.\",\n \"signature\": \"Update file signature invalid. Try downloading again.\",\n \"appLocation\": \"QwenPaw is running from a read-only macOS location. Move QwenPaw.app to the Applications folder, launch it from there, then try updating again.\",\n \"other\": \"Update failed.\"\n },\n \"updateLater\": \"Download in Background\",\n \"backgroundDownloading\": \"Downloading in background…\",\n \"readyToInstall\": \"Update ready\",\n \"readyToInstallHint\": \"v{{version}} update is downloaded. Restart to install and launch the new version.\",\n \"restartNow\": \"Update Now\",\n \"backgroundFailed\": \"Background download failed\"\n },\n \"settings\": {\n \"language\": \"Language\",\n \"theme\": \"Theme\",\n \"mode\": \"Mode\"\n },\n \"simpleMode\": \"Simple Mode\",\n \"fullMode\": \"Full Mode\"\n },\n \"debug\": {\n \"title\": \"Debug\",\n \"desc\": \"View the backend daemon log file to help diagnose issues. Logs refresh automatically while this page is open.\",\n \"level\": {\n \"all\": \"All\"\n },\n \"backend\": {\n \"title\": \"Backend logs\",\n \"autoRefresh\": \"Auto refresh\",\n \"newestFirst\": \"Newest first\",\n \"updatedAt\": \"Updated at\",\n \"path\": \"Log file\",\n \"notFound\": \"Backend log file was not found yet.\",\n \"placeholder\": \"Backend log output will appear here.\",\n \"loadFailed\": \"Failed to load backend logs\",\n \"searchPlaceholder\": \"Search backend logs...\"\n },\n \"actions\": {\n \"refreshBackend\": \"Refresh backend logs\",\n \"refreshSuccess\": \"Logs refreshed\",\n \"copyBackend\": \"Copy backend logs\"\n }\n },\n \"inbox\": {\n \"title\": \"Inbox\",\n \"tabApprovals\": \"Approvals\",\n \"tabPushMessages\": \"Push Messages\",\n \"tabHarvests\": \"AI Harvest\",\n \"summaryApprovals\": \"Approvals\",\n \"summaryPushUnread\": \"Unread Push\",\n \"summaryHarvests\": \"Harvests\",\n \"from\": \"From:\",\n \"skillAutoUpdateTitle\": \"Skill auto-sync\",\n \"skillPoolSender\": \"Skill Pool\",\n \"skillAutoUpdated\": \"Skill \\\"{{skill}}\\\" changed — auto-synced to: {{agents}}\",\n \"skillAutoUpdateFailed\": \"Skill \\\"{{skill}}\\\" auto-sync failed for: {{agents}}\",\n \"requestedBy\": \"By\",\n \"markRead\": \"Mark Read\",\n \"markAllRead\": \"Mark All Read\",\n \"markAllReadNoUnread\": \"No unread messages\",\n \"markAllReadSuccess\": \"Marked {{count}} messages as read\",\n \"selectAllCurrentPage\": \"Select current page\",\n \"selectedItems\": \"{{count}} selected\",\n \"batchDeleteButton\": \"Batch Delete\",\n \"batchOperation\": \"Batch\",\n \"exitBatch\": \"Exit Batch\",\n \"batchDeleteConfirm\": \"Delete {{count}} selected push messages?\",\n \"batchDeleteSuccess\": \"Deleted {{count}} push messages\",\n \"pushCronHeader\": \"Cron Job: {{name}}\",\n \"viewDetails\": \"View Details\",\n \"deleteMessageConfirm\": \"Delete this push message?\",\n \"viewAll\": \"View All\",\n \"read\": \"Read\",\n \"approve\": \"Approve\",\n \"reject\": \"Reject\",\n \"ready\": \"Ready\",\n \"harvestNow\": \"Harvest Now\",\n \"createHarvest\": \"Create Harvest\",\n \"createFirstHarvest\": \"Create Your First Harvest\",\n \"createSuccess\": \"Harvest created successfully\",\n \"emptyApprovals\": \"No pending approvals\",\n \"emptyPush\": \"No push messages\",\n \"wobbleEnable\": \"Enable approval wobble notification\",\n \"wobbleDisable\": \"Disable approval wobble notification\",\n \"emptyHarvests\": \"No harvests yet\",\n \"statusReadyToHarvest\": \"Ready to Harvest\",\n \"statusGrowing\": \"Growing\",\n \"harvestedTimes\": \"Harvested {{count}} times\",\n \"harvestSuccessRate\": \"Success Rate {{rate}}%\",\n \"messageDetailComingSoon\": \"Message detail view - Coming soon!\",\n \"harvestSettingsComingSoon\": \"Harvest settings - Coming soon!\",\n \"messageDetailTitle\": \"Execution Detail\",\n \"detailCronTitle\": \"Cron Job: {{name}}\",\n \"detailHeartbeatTitle\": \"Heartbeat\",\n \"messageNotFound\": \"Message not found\",\n \"detailTaskName\": \"Task Name\",\n \"detailTaskId\": \"Task ID\",\n \"detailStatus\": \"Status\",\n \"detailSeverity\": \"Severity\",\n \"detailAgent\": \"Agent\",\n \"detailSource\": \"Source\",\n \"detailSourceCronJob\": \"Cron Job\",\n \"detailSourceHeartbeat\": \"Heartbeat\",\n \"detailTrigger\": \"Trigger\",\n \"detailDuration\": \"Duration\",\n \"detailExecutedAt\": \"Executed At\",\n \"detailReceivedAt\": \"Received At\",\n \"detailRunId\": \"Run ID\",\n \"detailContent\": \"Output\",\n \"detailPayload\": \"Payload\",\n \"detailExecutionTrace\": \"Execution Trace\",\n \"detailTraceEmpty\": \"No execution trace available\",\n \"filterByAgent\": \"Filter by agent\",\n \"filterBySourceType\": \"Filter by source\",\n \"sourceTypeCron\": \"Cron\",\n \"sourceTypeHeartbeat\": \"Heartbeat\",\n \"sourceTypeMemory\": \"Memory\"\n },\n \"acp\": {\n \"title\": \"ACP\",\n \"loading\": \"Loading ACP configuration...\",\n \"builtin\": \"Built-in\",\n \"custom\": \"Custom\",\n \"create\": \"Add Custom Agent\",\n \"createTitle\": \"Create ACP Agent\",\n \"createSuccess\": \"ACP agent created\",\n \"editTitle\": \"Edit ACP Configuration\",\n \"agentKey\": \"Agent Key\",\n \"agentKeyRequired\": \"Please enter an agent key\",\n \"agentKeyInvalid\": \"Agent key can only contain letters, numbers, underscores, and hyphens\",\n \"agentKeyExists\": \"This agent key already exists\",\n \"enabled\": \"Enabled\",\n \"command\": \"Command\",\n \"commandRequired\": \"Please enter a command\",\n \"args\": \"Arguments\",\n \"argsHelp\": \"One argument per line\",\n \"env\": \"Environment Variables\",\n \"envHelp\": \"Use KEY=VALUE format on each line\",\n \"envInvalidLine\": \"Invalid environment variable format: {{line}}\",\n \"trusted\": \"Trusted\",\n \"toolParseMode\": \"Tool Parse Mode\",\n \"toolParseModeRequired\": \"Please select a tool parse mode\",\n \"stdioBufferLimit\": \"Stdio Buffer Limit\",\n \"stdioBufferLimitHelp\": \"Maximum stdio line buffer size in bytes for the ACP subprocess.\",\n \"stdioBufferLimitRequired\": \"Please enter a stdio buffer limit\",\n \"stdioBufferLimitMin\": \"Stdio buffer limit must be at least 1 byte\",\n \"docs\": \"Setup Docs\",\n \"docsHelp\": \"Open the ACP integration docs and jump to the \\\"How to configure external runners\\\" section\",\n \"notSet\": \"Not set\",\n \"configSaved\": \"ACP configuration saved\",\n \"configFailed\": \"Failed to save ACP configuration\",\n \"deleteTitle\": \"Delete {{name}}\",\n \"deleteConfirm\": \"Delete this ACP agent? This action cannot be undone.\",\n \"deleteSuccess\": \"ACP agent deleted\",\n \"deleteFailed\": \"Failed to delete ACP agent\",\n \"nodeSettings\": \"Node Settings\",\n \"nodePath\": \"Node path\",\n \"chooseOtherNode\": \"Choose another Node...\",\n \"selectNodePath\": \"Select Node path\",\n \"nodePathPrompt\": \"Enter the node executable path\",\n \"nodeSaved\": \"Node settings saved\",\n \"nodeLoadFailed\": \"Failed to load Node settings\",\n \"nodeSaveFailed\": \"Node path is unavailable\",\n \"nodeRuntime\": {\n \"bundled\": \"Bundled Node\",\n \"system\": \"System Node\",\n \"custom\": \"Custom Node\"\n },\n \"nodeRuntimeReason\": {\n \"systemNodeMissing\": \"System Node was not detected\",\n \"nodeMissing\": \"Node path does not exist\",\n \"npxMissing\": \"npx was not found\",\n \"versionCheckFailed\": \"Version check failed\",\n \"unavailable\": \"Unavailable\"\n }\n },\n \"voiceTranscription\": {\n \"title\": \"Voice Transcription\",\n \"description\": \"Configure how incoming audio and voice messages are handled.\",\n \"saveSuccess\": \"Audio mode saved\",\n \"saveFailed\": \"Failed to save audio mode\",\n \"audioModeLabel\": \"Audio Mode\",\n \"audioModeDescription\": \"Choose how voice messages from channels (Discord, Telegram, etc.) are processed before being sent to the model.\",\n \"modeAuto\": \"Auto (Recommended)\",\n \"modeAutoDesc\": \"Transcribe audio to text using the selected transcription provider, then send the text to the model. If transcription is unavailable or disabled, a file-uploaded placeholder is shown instead. Audio is never sent directly to the model in this mode. Works with all models.\",\n \"modeNative\": \"Native Audio\",\n \"modeNativeDesc\": \"Send the audio file directly to the model without transcription. This is the only mode that sends audio to the model. Only works with specific audio-capable models (e.g. gpt-4o-audio). Most models do not support this and will reject the message.\",\n \"ffmpegReady\": \"ffmpeg is installed. Audio conversion is available for native mode.\",\n \"ffmpegMissing\": \"ffmpeg is not installed.\",\n \"ffmpegMissingDesc\": \"Native audio mode requires ffmpeg to convert audio formats (e.g. .ogg to .wav). Install ffmpeg as a system package to enable this mode.\",\n \"providerTypeLabel\": \"Transcription Provider\",\n \"providerTypeDescription\": \"Choose the transcription backend. Select Disabled if you do not need voice transcription.\",\n \"providerTypeDisabled\": \"Disabled\",\n \"providerTypeDisabledDesc\": \"No transcription. Voice messages will show a file-uploaded placeholder.\",\n \"providerTypeWhisperApi\": \"Whisper API\",\n \"providerTypeWhisperApiDesc\": \"Use an OpenAI-compatible Whisper API endpoint from a configured provider (e.g. OpenAI, Ollama).\",\n \"providerTypeLocalWhisper\": \"Local Whisper\",\n \"providerTypeLocalWhisperDesc\": \"Run transcription locally using the openai-whisper Python library. Requires both ffmpeg and openai-whisper to be installed.\",\n \"localWhisperReady\": \"Local Whisper is ready. Both ffmpeg and openai-whisper are installed.\",\n \"localWhisperMissing\": \"Local Whisper is not ready. Missing dependencies must be installed.\",\n \"localWhisperMissingDesc\": \"ffmpeg: {{ffmpeg}} | openai-whisper: {{whisper}}. Install missing dependencies: ffmpeg (system package) and openai-whisper (uv pip install openai-whisper, or install QwenPaw with the [whisper] extra).\",\n \"providerLabel\": \"Whisper API Provider\",\n \"providerDescription\": \"Select which provider to use for audio transcription via the Whisper API. Only providers with a Whisper-compatible endpoint are shown.\",\n \"providerPlaceholder\": \"Select a provider...\",\n \"noProvidersWarning\": \"No transcription-capable providers found. Configure an OpenAI provider to enable voice transcription.\",\n \"transcriptionInfoTitle\": \"How transcription works\",\n \"transcriptionInfoDesc\": \"Whisper API transcription uses an OpenAI-compatible /v1/audio/transcriptions endpoint. This requires a configured provider with a Whisper-compatible endpoint — for example, an OpenAI provider. Select a specific provider above to enable transcription.\",\n \"transcriptionInfoDescLocal\": \"Local Whisper transcription runs the openai-whisper library directly on your machine. It requires both ffmpeg (for audio decoding) and the openai-whisper Python package to be installed. No API key or network connection is needed. Install with: uv pip install 'qwenpaw[whisper]'.\"\n },\n \"backup\": {\n \"title\": \"Backups\",\n \"description\": \"Backup and restore agent workspaces and configurations.\",\n \"create\": \"Create Backup\",\n \"import\": \"Import\",\n \"export\": \"Export\",\n \"restore\": \"Restore\",\n \"delete\": \"Delete\",\n \"batchDelete\": \"Delete Selected\",\n \"batchDeleteConfirm\": \"Are you sure you want to delete {{count}} backup(s)? This action cannot be undone.\",\n \"deleteConfirm\": \"Are you sure you want to delete this backup? This action cannot be undone.\",\n \"name\": \"Name\",\n \"createdAt\": \"Created\",\n \"scopeSummary\": \"Scope\",\n \"descriptionLabel\": \"Description\",\n \"noBackups\": \"No backups yet. Create one to backup your current configuration.\",\n \"loadFailed\": \"Failed to load backups\",\n \"createTitle\": \"Create Backup\",\n \"backupMode\": \"Backup Mode\",\n \"fullBackup\": \"Full Backup\",\n \"fullBackupDesc\": \"Backup everything including all agent workspaces, global settings, skill pool, and secrets\",\n \"customBackup\": \"Custom Backup\",\n \"partialBackup\": \"Partial Backup\",\n \"partialBackupDesc\": \"Customize what to backup\",\n \"nameRequired\": \"Backup name is required\",\n \"namePlaceholder\": \"Enter backup name\",\n \"descriptionPlaceholder\": \"Optional description...\",\n \"scopeAgents\": \"Agent Workspaces\",\n \"scopeGlobalConfig\": \"Global Settings\",\n \"scopeSkillPool\": \"Skill Pool\",\n \"scopeSecrets\": \"Secrets\",\n \"scopeSecretsHint\": \"Includes model provider secrets (API keys), environment variables, and other sensitive information\",\n \"securityNotice\": \"This backup may contain sensitive credentials. Agent workspaces include channel credentials (bot tokens, app secrets, etc.), and model provider secrets include API keys. Do not share backup files with others.\",\n \"searchPlaceholder\": \"Search backups by name or ID...\",\n \"total\": \"Total {{count}}\",\n \"agentsPlaceholder\": \"Select agent workspaces (leave empty for all)\",\n \"selectAll\": \"Select All\",\n \"deselectAll\": \"Deselect All\",\n \"createSuccess\": \"Backup created successfully\",\n \"createFailed\": \"Failed to create backup\",\n \"restoreTitle\": \"Restore Backup\",\n \"restoreScope\": \"Restore Scope\",\n \"restoreFullWarning\": \"Full restore will completely replace all contents of the current instance (including all agents, global config, skill pool, and secrets). This action cannot be undone. Related features will be unavailable during restore. Please restart the service after restoration is complete.\",\n \"agentGroupExisting\": \"Existing\",\n \"agentGroupNew\": \"New\",\n \"agentExists\": \"Exists\",\n \"agentNew\": \"New\",\n \"agentActionReplace\": \"Overwrite\",\n \"agentActionAdd\": \"New\",\n \"agentColumnName\": \"Agent\",\n \"agentColumnWorkspace\": \"Workspace\",\n \"agentTotal\": \"{{count}} agents total\",\n \"noAgentsInBackup\": \"No agent workspaces in this backup\",\n \"loadingAgents\": \"Loading agent list...\",\n \"agentSearchPlaceholder\": \"Search by name or ID...\",\n \"agentSearchTotal\": \"{{count}} of {{total}} agents\",\n \"restoreCustomSummary\": \"{{existing}} existing · {{added}} new selected\",\n \"defaultWorkspaceDirUnused\": \"No new agents selected\",\n \"otherOptions\": \"Other Options\",\n \"defaultWorkspaceDirDefault\": \"<Default path>/{{aid}}\",\n \"restoreWarningModify\": \"Restoring will modify current configurations. This action cannot be undone.\",\n \"restoreWarningRestart\": \"Related features will be unavailable during restore. Please restart the service after restoration is complete.\",\n \"preRestoreBackupTitle\": \"Create Pre-Restore Backup\",\n \"preRestoreBackupContent\": \"The restore operation is irreversible. Would you like to create a backup of the current state first? This allows you to rollback if any issues occur during restoration.\",\n \"preRestoreBackupYes\": \"Yes, create backup first\",\n \"preRestoreBackupNo\": \"No, restore directly\",\n \"preRestoreBackupDesc\": \"Auto-created backup before restore\",\n \"preRestoreBackupFailed\": \"Failed to create pre-restore backup\",\n \"preRestoreBackupSuccess\": \"Pre-restore backup created successfully, ID: {{id}}\",\n \"creatingPreRestoreBackup\": \"Creating pre-restore backup...\",\n \"skipAndRestore\": \"Skip and continue to restore\",\n \"restoreConfirm\": \"I confirm that I want to restore this backup\",\n \"restoreMode\": \"Restore Mode\",\n \"restoreModeFull\": \"Full Restore\",\n \"restoreModeFullDesc\": \"Completely replace all contents of the current instance, including all agents, global config, skill pool and secrets\",\n \"restoreModeCustom\": \"Custom Restore\",\n \"restoreModeCustomDesc\": \"Select the contents to restore, such as specific agents, config, etc.; existing agents not included in this restore will be preserved\",\n \"restoreModeFullDisabled\": \"Full backup only\",\n \"defaultWorkspaceDir\": \"Agent Default Workspace Directory\",\n \"defaultWorkspaceDirPlaceholder\": \"Leave empty to use ~/.qwenpaw/workspaces\",\n \"defaultWorkspaceDirHint\": \"If the agent's original workspace path does not exist, the agent will be saved under <default path>/<agent ID>. If it exists, the agent's original workspace path will be used.\",\n \"restoreStrategy\": \"Restore strategy\",\n \"restoreStrategyPreserve\": \"Preserve local security and MCP\",\n \"restoreStrategyPreserveDesc\": \"Keep this instance's security guards and MCP configuration.\",\n \"restoreStrategyRestore\": \"Restore these settings from backup\",\n \"restoreStrategyRestoreDesc\": \"Use the backup's security and MCP configuration.\",\n \"restoreSuccess\": \"Backup restored successfully. Please restart the service.\",\n \"restoreSuccessPreserved\": \"Backup restored successfully. Preserved local settings: {{keys}}. Please restart the service.\",\n \"restoreSuccessWithBackup\": \"Backup restored successfully. Pre-restore backup ID: {{preRestoreId}}. Please restart the service.\",\n \"restoreFailed\": \"Failed to restore backup\",\n \"restoreTargetBusy\": \"Restore failed because these directories are still in use. Close the browser or process using them, then retry. If they remain locked, restart the system and try again.\",\n \"detailLoadFailed\": \"Failed to load backup details; restore is unavailable\",\n \"trustLocalBanner\": \"Local backup - full restore by default\",\n \"trustForeignBanner\": \"Imported backup - local security and MCP are preserved by default\",\n \"trustLegacyBanner\": \"Legacy backup - trust confirmation is required before restore\",\n \"trustForeignTitle\": \"Trust this backup?\",\n \"trustLegacyTitle\": \"Trust legacy backup?\",\n \"unknownBackupName\": \"Backup archive\",\n \"trustForeignDesc\": \"This backup was not signed by this instance. Only continue if you trust the source; local security and MCP settings will be preserved by default when restored.\",\n \"trustLegacyDesc\": \"This older backup has no local signature. Only continue if you trust where it came from; this instance will sign it before restore.\",\n \"deleteSuccess\": \"Backup(s) deleted successfully\",\n \"deleteFailed\": \"Failed to delete backup(s)\",\n \"exportFailed\": \"Failed to export backup\",\n \"exportWarningTitle\": \"Sensitive Information Warning\",\n \"exportWarningContent\": \"This backup may contain sensitive credential information. Agent workspaces contain channel credentials (such as bot tokens, app secrets, etc.), and model provider secrets contain API Keys. Do not share backup files with others.\",\n \"exportConfirm\": \"Confirm Export\",\n \"importTitle\": \"Import Backup\",\n \"importSuccess\": \"Backup imported successfully\",\n \"importFailed\": \"Failed to import backup\",\n \"importConflictTitle\": \"Backup Already Exists\",\n \"importConflictDesc\": \"A backup with the same ID already exists. Do you want to overwrite it?\",\n \"importReplace\": \"Overwrite\",\n \"agents\": \"{{count}} Agent(s)\",\n \"allAgents\": \"All Agents\",\n \"globalConfig\": \"Global Settings\",\n \"skillPool\": \"Skill Pool\",\n \"secrets\": \"Secrets\",\n \"selected\": \"{{count}} selected\",\n \"fileCount\": \"{{count}} files\",\n \"totalSize\": \"{{size}}\",\n \"progressStarting\": \"Starting backup...\",\n \"progressAgent\": \"Backing up agent {{index}} / {{total}}...\",\n \"progressSaving\": \"Saving backup file...\",\n \"progressDone\": \"Backup complete\",\n \"localModelsNotice\": \"Backup files do not include local model files. For cross-device migration, you will need to re-download the required local models on the target device.\"\n },\n \"checkpoints\": {\n \"nav\": \"Checkpoints\",\n \"title\": \"State Checkpoints\",\n \"auto\": \"Auto\",\n \"autoEnabled\": \"Automatic checkpoints enabled\",\n \"autoDisabled\": \"Automatic checkpoints disabled\",\n \"refresh\": \"Refresh checkpoints\",\n \"snapshot\": \"Snapshot\",\n \"snapshotCreated\": \"Snapshot created\",\n \"graph\": \"Graph\",\n \"checkpoint\": \"Checkpoint\",\n \"type\": \"Type\",\n \"commit\": \"Commit\",\n \"createdAt\": \"Created\",\n \"session\": \"Session\",\n \"channel\": \"Channel\",\n \"parent\": \"Parent\",\n \"details\": \"Checkpoint details\",\n \"search\": \"Search query, name, session, or SHA\",\n \"allTypes\": \"All types\",\n \"allSessions\": \"All sessions\",\n \"showingLatest\": \"Latest {{count}} shown\",\n \"loadFailed\": \"Unable to load checkpoints\",\n \"retry\": \"Retry\",\n \"more\": \"More actions\",\n \"noMatches\": \"No checkpoints match these filters.\",\n \"empty\": \"No state checkpoints yet. Your version history will appear here.\",\n \"kind\": {\n \"auto\": \"Auto\",\n \"snapshot\": \"Snapshot\",\n \"safety\": \"Safety\",\n \"commit\": \"Commit\"\n },\n \"summary\": { \"total\": \"checkpoints\" },\n \"snapshotDialog\": {\n \"title\": \"Create snapshot\",\n \"session\": \"Session\",\n \"name\": \"Name\",\n \"placeholder\": \"Optional snapshot name\"\n },\n \"gc\": {\n \"settingsAction\": \"Automatic cleanup settings\",\n \"settingsTitle\": \"Automatic cleanup settings\",\n \"keepCount\": \"Automatic checkpoints to keep\",\n \"keepDays\": \"Automatic checkpoint retention (days)\",\n \"preRestoreDays\": \"Pre-restore snapshot retention (days)\",\n \"settingsSaved\": \"Automatic cleanup settings saved\",\n \"thoroughAction\": \"Thoroughly compact checkpoints\",\n \"thoroughTitle\": \"Thoroughly compact checkpoints?\",\n \"thoroughDescription\": \"Removes {{count}} checkpoint(s), ignoring auto-checkpoint retention. Session HEADs are kept.\",\n \"thoroughConfirm\": \"Thoroughly compact\",\n \"thoroughSuccess\": \"Removed {{count}} checkpoint(s) during thorough compaction\",\n \"action\": \"Clean up checkpoints\",\n \"title\": \"Clean up checkpoints using retention policy?\",\n \"description\": \"Removes {{count}} expired checkpoint(s) under the current retention policy. Session HEADs are kept.\",\n \"confirm\": \"Clean up\",\n \"success\": \"Removed {{count}} checkpoint(s)\"\n },\n \"reset\": {\n \"action\": \"Reset checkpoint data\",\n \"title\": \"Reset all checkpoint data?\",\n \"description\": \"This permanently removes every checkpoint and turns automatic checkpoints off.\",\n \"confirm\": \"Reset\",\n \"success\": \"Checkpoint data reset\"\n },\n \"restore\": {\n \"action\": \"Restore\",\n \"title\": \"Restore checkpoint\",\n \"preview\": \"Preview restore\",\n \"confirm\": \"Restore checkpoint\",\n \"conversation\": \"Conversation\",\n \"memory\": \"Memory\",\n \"files\": \"Workspace files\",\n \"refreshWarning\": \"The conversation will change. Refresh the conversation after the restore completes.\",\n \"selectedCount\": \"{{count}} files selected\",\n \"selectAll\": \"Select all changed files\",\n \"delete\": \"Delete\",\n \"restore\": \"Restore\",\n \"noFileChanges\": \"No workspace file changes are needed.\",\n \"success\": \"Checkpoint restored. Refresh the conversation to load its restored state.\"\n }\n },\n \"workspace\": {\n \"title\": \"Files\",\n \"workspacePath\": \"Workspace:\",\n \"noFiles\": \"No files\",\n \"coreFiles\": \"Core Files\",\n \"coreFilesDesc\": \"Bootstrap persona, identity, and tool guidance.\",\n \"uploadTooltip\": \"ZIP files only\",\n \"uploadTooltipWithLimit\": \"ZIP files only, max {{limit}}MB\",\n \"selectFile\": \"Select a file to edit\",\n \"fileContent\": \"File content...\",\n \"uploadSuccess\": \"File uploaded successfully\",\n \"uploadFailed\": \"File upload failed\",\n \"downloadPreparing\": \"Preparing workspace download...\",\n \"downloadSuccess\": \"Workspace downloaded successfully\",\n \"downloadFailed\": \"Workspace download failed\",\n \"zipOnly\": \"Only .zip files are supported for upload\",\n \"fileSizeExceeded\": \"File size exceeds {{limit}}MB limit. Current file: {{size}}MB\",\n \"systemPromptToggleTooltip\": \"Enable/disable this file in system prompt\",\n \"memoryFileWarning\": \"MEMORY.md is typically queried on-demand by the agent using tools. Loading it into the system prompt may cause the context to become too long.\",\n \"configUpdated\": \"System prompt configuration updated\",\n \"configUpdateFailed\": \"Failed to update system prompt configuration\",\n \"attribution\": \"Workspace design partly inspired by the OpenClaw project — thank you! 🐾\",\n \"saveSuccess\": \"Saved successfully\",\n \"saveFailed\": \"Save failed\",\n \"saving\": \"Saving...\",\n \"saved\": \"Saved\",\n \"unsaved\": \"Unsaved\"\n },\n \"agent\": {\n \"parent\": \"Settings\",\n \"agents\": \"Agents\",\n \"management\": \"Agent Management\",\n \"pageDescription\": \"Create, configure, and manage multiple AI agents with custom workspaces and identities.\",\n \"name\": \"Name\",\n \"id\": \"ID\",\n \"description\": \"Description\",\n \"workspace\": \"Workspace Path\",\n \"create\": \"Create Agent\",\n \"createTitle\": \"Create New Agent\",\n \"createSuccess\": \"Agent created successfully\",\n \"createFailed\": \"Failed to create agent\",\n \"edit\": \"Edit\",\n \"editTitle\": \"Edit Agent - {{name}}\",\n \"updateSuccess\": \"Agent updated successfully\",\n \"updateFailed\": \"Failed to update agent\",\n \"delete\": \"Delete\",\n \"deleteConfirm\": \"Confirm Delete Agent\",\n \"deleteConfirmDesc\": \"Agent will be unavailable after deletion, but workspace files will be kept\",\n \"deleteSuccess\": \"Agent deleted successfully\",\n \"deleteFailed\": \"Failed to delete agent\",\n \"selectAgent\": \"Select Agent\",\n \"defaultDisplayName\": \"Default Agent\",\n \"currentWorkspace\": \"Current Agent\",\n \"switchSuccess\": \"Agent switched successfully\",\n \"switchFailed\": \"Failed to switch agent\",\n \"loadFailed\": \"Failed to load agent list\",\n \"loadConfigFailed\": \"Failed to load agent config\",\n \"saveFailed\": \"Failed to save agent\",\n \"idRequired\": \"Please enter agent ID\",\n \"idLabel\": \"Agent ID (optional)\",\n \"idHelp\": \"Leave empty to auto-generate. Only letters, digits, hyphens and underscores allowed.\",\n \"idPattern\": \"ID can only contain letters, numbers, underscores and hyphens\",\n \"idPlaceholder\": \"e.g.: my-agent\",\n \"nameRequired\": \"Please enter agent name\",\n \"namePlaceholder\": \"e.g.: My Agent\",\n \"descriptionPlaceholder\": \"Briefly describe this agent's purpose...\",\n \"workspaceHelp\": \"Leave empty to auto-generate in ~/.qwenpaw/workspaces/<id>\",\n \"dragHandleTooltip\": \"Drag to reorder agents\",\n \"reorderSuccess\": \"Agent order saved\",\n \"reorderFailed\": \"Failed to save agent order\",\n \"defaultNotEditable\": \"Default agent cannot be edited\",\n \"defaultNotDeletable\": \"Default agent cannot be deleted\",\n \"disable\": \"Disable\",\n \"enable\": \"Enable\",\n \"disabled\": \"Disabled\",\n \"disableConfirm\": \"Confirm Disable Agent\",\n \"disableConfirmDesc\": \"The agent instance will not start after disabling, but will remain visible in the list\",\n \"enableConfirm\": \"Confirm Enable Agent\",\n \"enableConfirmDesc\": \"The agent will be available for switching after enabling\",\n \"disableSuccess\": \"Agent disabled successfully\",\n \"enableSuccess\": \"Agent enabled successfully\",\n \"toggleFailed\": \"Failed to toggle agent state\",\n \"enableAgent\": \"Enable {{name}}\",\n \"disableAgent\": \"Disable {{name}}\",\n \"disabledAgents\": \"Disabled ({{count}})\",\n \"pinnedAgents\": \"Pinned\",\n \"otherAgents\": \"Other agents\",\n \"pinned\": \"Pinned\",\n \"defaultPinned\": \"The default agent is always pinned\",\n \"longPressToPin\": \"Press and hold to pin\",\n \"longPressToUnpin\": \"Press and hold to unpin\",\n \"pinAgent\": \"Pin agent\",\n \"unpinAgent\": \"Unpin agent\",\n \"pinSuccess\": \"Agent pinned\",\n \"unpinSuccess\": \"Agent unpinned\",\n \"pinFailed\": \"Failed to update pinned state\",\n \"status\": {\n \"disabled\": \"Disabled\",\n \"pending\": \"Waiting to start\",\n \"starting\": \"Starting\",\n \"running\": \"Running\",\n \"failed\": \"Startup failed\",\n \"waitUntilStarted\": \"Available after startup completes\"\n },\n \"defaultNotDisablable\": \"Default agent cannot be disabled\",\n \"currentAgentDeleted\": \"Current agent has been deleted, automatically switched to default agent\",\n \"currentAgentDisabled\": \"Current agent has been disabled, automatically switched to default agent\",\n \"switchedToDefault\": \"Automatically switched to default agent\",\n \"cannotSwitchToDisabled\": \"This agent is disabled, please enable it in the management page first\",\n \"initialSkills\": \"Initial Skills\",\n \"initialSkillsHelp\": \"Select skills to add from the pool. You can add or remove skills later.\",\n \"selectAll\": \"All\",\n \"selectBuiltin\": \"Builtin\",\n \"selectNone\": \"None\",\n \"noPoolSkills\": \"No skills available in pool\",\n \"addSkillsToAgent\": \"Add Skills\",\n \"backend\": {\n \"column\": \"Backend\",\n \"eyebrow\": \"Agent architecture\",\n \"typeTitle\": \"Choose how this agent works\",\n \"typeDescription\": \"The agent type controls every Chat, Coding, and Channel conversation.\",\n \"nativeTitle\": \"QwenPaw native agent\",\n \"nativeBadge\": \"Native\",\n \"nativeDescription\": \"Uses your configured LLM, Skills, tools, memory, and QwenPaw runtime.\",\n \"thirdPartyTitle\": \"Third-party agent\",\n \"thirdPartyBadge\": \"Third-party\",\n \"thirdPartyDescription\": \"Uses an authorized external agent runtime instead of a QwenPaw model.\",\n \"providerTitle\": \"Choose a third-party agent\",\n \"providerDescription\": \"Authentication is shared on this device; projects and conversations stay isolated per agent.\",\n \"codexHint\": \"ChatGPT OAuth · Agent workspace\",\n \"qoderHint\": \"Qoder account or PAT · Agent workspace\",\n \"account\": \"Codex account\",\n \"qoderAccount\": \"Qoder account\",\n \"binary\": \"Codex executable\",\n \"binaryHelp\": \"Leave blank to use the Codex CLI bundled with the Python SDK or detect a standalone installation. Otherwise enter the full path to codex on macOS/Linux or codex.exe on Windows. Binaries inside the ChatGPT app or editor extensions are not supported. QwenPaw also reads CODEX_BINARY and the backend PATH.\",\n \"binaryPlaceholder\": \"Auto-detect, or paste the Codex executable path\",\n \"qoderBinary\": \"Qoder executable\",\n \"qoderBinaryHelp\": \"Leave blank to use the qodercli bundled with the Python SDK or detect an existing installation. Otherwise enter the full path to qodercli on macOS/Linux or qodercli.exe on Windows. QwenPaw also reads QODERCLI_PATH and the backend PATH.\",\n \"qoderBinaryPlaceholder\": \"Auto-detect, or paste the qodercli executable path\",\n \"detect\": \"Detect\",\n \"detectedBinary\": \"Codex detected\",\n \"qoderDetectedBinary\": \"Qoder detected\",\n \"apiKeyLoginHint\": \"You can also authenticate in a terminal with: codex login --with-api-key\",\n \"qoderAuthHint\": \"Connect opens Qoder browser login. You can also run qodercli login or set QODER_PERSONAL_ACCESS_TOKEN before starting the backend.\",\n \"qoderConnect\": \"Connect to Qoder\",\n \"model\": \"Agent model\",\n \"modelDefault\": \"Use the agent default\",\n \"defaultBadge\": \"Default\",\n \"reasoningEffort\": \"Reasoning effort\",\n \"reasoningDefault\": \"Use the model default\",\n \"codexNotFound\": \"Codex runtime not found. Install qwenpaw[codex] or provide a standalone Codex CLI.\",\n \"qoderNotFound\": \"Qoder runtime not found. Install qwenpaw[qoder] or provide qodercli.\",\n \"chatSettingsHint\": \"Choose the model, reasoning effort, and execution permissions from the Chat toolbar.\",\n \"capabilityTitle\": \"QwenPaw capabilities\",\n \"inheritedSkills\": \"Skills\",\n \"inheritedMcp\": \"MCP servers\",\n \"runtimeInherited\": \"Inherited at runtime · new sessions\",\n \"notSupported\": \"Not supported by this provider\",\n \"policyCompatibility\": \"MCP policy\",\n \"toolAllowlistSupported\": \"Tool allowlist supported\",\n \"policyLimited\": \"Provider limitations apply\",\n \"chatModelHint\": \"Agent-scoped settings · applied on the next turn\",\n \"appliesNextTurn\": \"Applies on the next turn\",\n \"approvalMode\": \"Execution permissions\",\n \"approvalPresets\": {\n \"ask\": {\n \"name\": \"Ask before changes\",\n \"description\": \"Allow workspace changes and ask before elevated actions.\"\n },\n \"read-only\": {\n \"name\": \"Read only\",\n \"description\": \"Inspect files without changing them.\"\n },\n \"workspace\": {\n \"name\": \"Workspace access\",\n \"description\": \"Allow workspace changes without confirmation.\"\n },\n \"accept-edits\": {\n \"name\": \"Accept edits\",\n \"description\": \"Allow file edits while keeping other safeguards.\"\n },\n \"plan\": {\n \"name\": \"Plan only\",\n \"description\": \"Analyze and plan without changing files.\"\n },\n \"auto\": {\n \"name\": \"Automatic\",\n \"description\": \"Let Qoder decide which safe actions can run.\"\n },\n \"full-access\": {\n \"name\": \"Full access\",\n \"description\": \"Allow unrestricted local execution without confirmation.\"\n }\n }\n },\n \"model\": \"Model\",\n \"modelHelp\": \"Assign a specific LLM model to this agent. Leave empty to use the global default.\",\n \"modelPlaceholder\": \"Use global default\",\n \"noConfiguredModels\": \"No configured models\",\n \"modelColumn\": \"Model\",\n \"copyTooltip\": \"Copy agent configuration\",\n \"copyDefaultTooltip\": \"Copy using the default agent as a template\",\n \"copyTitle\": \"Copy Agent Configuration\",\n \"copyNameLabel\": \"New agent name\",\n \"copyOptionsLabel\": \"Copy content\",\n \"copyOptionAgentJson\": \"agent.json (required)\",\n \"copyOptionAgentJsonHint\": \"Always copies the parsed source config and resets ID, name, workspace, and channels. This is not a byte-for-byte copy of the on-disk agent.json.\",\n \"copyOptionMdFiles\": \"AGENTS / SOUL / PROFILE / HEARTBEAT / BOOTSTRAP.md\",\n \"copyOptionSkills\": \"skills/ + manifest\",\n \"copyOptionJobs\": \"jobs.json\",\n \"copyOptionJobsHint\": \"Copies job definitions only, not run history. The new agent will start running them on schedule after creation.\",\n \"copySuccess\": \"Agent configuration copied successfully\",\n \"copyFailed\": \"Failed to copy agent configuration\"\n },\n \"skills\": {\n \"title\": \"Skills\",\n \"description\": \"Manage agent skills and capabilities.\",\n \"qwenpawManaged\": \"QwenPaw managed\",\n \"qwenpawManagedHint\": \"Editable Skills projected into supported third-party agents at runtime.\",\n \"providerManaged\": \"Third-party agent Skills\",\n \"providerManagedHint\": \"Discovered from the current third-party agent and shown read-only.\",\n \"providerOnly\": \"Current third-party agent only\",\n \"readOnly\": \"Read only\",\n \"noDescription\": \"No description provided.\",\n \"providerSkillDetails\": \"Third-party agent Skill details\",\n \"providerManagedDetailHint\": \"This Skill is managed by the third-party agent. QwenPaw can display and use it, but cannot modify it.\",\n \"provider\": \"Third-party agent\",\n \"scope\": \"Scope\",\n \"statusLabel\": \"Status\",\n \"descriptionLabel\": \"Description\",\n \"importHub\": \"Upload via URL\",\n \"supportedSources\": \"Supported sources:\",\n \"importSkills\": \"Import Skill\",\n \"enterSkillUrl\": \"Enter Skill URL\",\n \"supportedSkillUrlSources\": \"Supported Skill Marketplaces:\",\n \"urlExamples\": \"URL examples:\",\n \"urlValid\": \"Valid URL from {{source}}\",\n \"invalidUrl\": \"Invalid URL\",\n \"clickToFill\": \"Click to fill in URL\",\n \"invalidSkillUrlSource\": \"Please verify the skill URL is from a supported marketplace\",\n \"type\": \"Type\",\n \"installedFrom\": \"Installed from\",\n \"source\": \"Source\",\n \"path\": \"Path\",\n \"tags\": \"Tags\",\n \"builtin\": \"Built-in\",\n \"custom\": \"Custom\",\n \"addSkill\": \"Add Skill\",\n \"createSkill\": \"Create Skill\",\n \"viewSkill\": \"View Skill\",\n \"editSkill\": \"Edit Skill\",\n \"skillName\": \"Skill Name\",\n \"skillContent\": \"Skill Content\",\n \"pleaseInputName\": \"Please input skill name\",\n \"pleaseInputContent\": \"Please input skill content\",\n \"skillNamePlaceholder\": \"e.g., weather_query\",\n \"contentPlaceholder\": \"[Format]\\n---\\nname: skill_name (required, lowercase with underscores)\\ndescription: Brief description (required)\\n---\\n\\nSkill implementation (Markdown format)\\n\\n[Example]\\n---\\nname: weather_query\\ndescription: Query weather info for a city\\n---\\n\\n## Features\\nQuery real-time weather data.\\n\\n## Usage\\nUser inputs city name, returns weather info.\",\n \"createSuccess\": \"Skill created successfully\",\n \"createFailed\": \"Failed to create skill\",\n \"deleteConfirm\": \"Are you sure you want to delete this skill?\",\n \"deleteSuccess\": \"Skill deleted successfully\",\n \"deleteFailed\": \"Failed to delete skill\",\n \"batchEnable\": \"Enable\",\n \"batchDisable\": \"Disable\",\n \"batchEnableSuccess\": \"{{count}} skills enabled\",\n \"batchEnablePartial\": \"{{enabled}} enabled, {{failed}} failed\",\n \"batchEnableFailed\": \"Batch enable failed\",\n \"batchDisableSuccess\": \"{{count}} skills disabled\",\n \"batchDisablePartial\": \"{{disabled}} disabled, {{failed}} failed\",\n \"batchDisableFailed\": \"Batch disable failed\",\n \"batchDeleteTitle\": \"Delete {{count}} skills?\",\n \"batchDeleteSuccess\": \"{{count}} skills deleted\",\n \"batchDeletePartial\": \"{{deleted}} deleted, {{failed}} failed\",\n \"batchDeleteFailed\": \"Batch delete failed\",\n \"selectedCount\": \"{{count}} selected\",\n \"updateSuccess\": \"Skill updated successfully\",\n \"updateFailed\": \"Failed to update skill\",\n \"frontmatterRequired\": \"Skills must start and end with ---\",\n \"frontmatterNameRequired\": \"Skills missing required field : name\",\n \"frontmatterDescriptionRequired\": \"Skills missing required field : description\",\n \"editNotSupported\": \"Failed to edit skill\",\n \"editNote\": \"Tip: You can edit skill content, channels, and config here.\",\n \"create\": \"Create\",\n \"optimizeWithAI\": \"AI Optimize\",\n \"stopOptimize\": \"Stop\",\n \"optimizeSuccess\": \"Skill optimized successfully\",\n \"optimizeFailed\": \"Failed to optimize skill\",\n \"noContentToOptimize\": \"No content to optimize\",\n \"cancelImport\": \"Cancel Import\",\n \"importCancelled\": \"Skill import cancelled\",\n \"importTimeout\": \"Skill import timed out. Please retry or check your network.\",\n \"uploadSkill\": \"Upload Skill\",\n \"uploadZip\": \"Upload via Zip\",\n \"uploadSuccess\": \"Skill uploaded successfully\",\n \"uploadFailed\": \"Skill upload failed\",\n \"uploadNoChange\": \"No new skills were imported. They may already exist.\",\n \"zipOnly\": \"Only .zip files are supported for upload\",\n \"fileSizeExceeded\": \"File size exceeds {{limit}}MB limit. Current file: {{size}}MB\",\n \"channels\": \"Channels\",\n \"allChannels\": \"All\",\n \"lastUpdated\": \"Updated\",\n \"searchPlaceholder\": \"Filter by name\",\n \"listView\": \"List view\",\n \"gridView\": \"Grid view\",\n \"refreshHint\": \"Refresh skills from disk\",\n \"poolSync\": \"Sync Status\",\n \"uploadedToPool\": \"Uploaded to skill pool\",\n \"downloadedToWorkspace\": \"Downloaded to current workspace\",\n \"builtinUpgradeTitle\": \"Upgrade Built-in Skill\",\n \"builtinUpgradeContent\": \"A newer version of built-in skill \\\"{{name}}\\\" is available in the pool. Overwrite the current version?\",\n \"languageSwitchTitle\": \"Switch Skill Language\",\n \"languageSwitchContent\": \"The following skills have a different language version in the pool and will be overwritten:\",\n \"overwriteExistingList\": \"The following skills already exist and will be overwritten:\",\n \"nameConflict\": \"Name conflict detected. Suggested rename has been filled.\",\n \"uploadToPoolHint\": \"Upload selected workspace skills to the shared local skill pool\",\n \"downloadFromPool\": \"Load from Skill Pool\",\n \"uploadToPool\": \"Sync to Skill Pool\",\n \"selectWorkspaceSkill\": \"Select current workspace skill\",\n \"selectPoolItem\": \"Select skill pool item\",\n \"renameOptional\": \"Rename (optional)\",\n \"renamePlaceholder\": \"Suggested rename will appear here on conflict\",\n \"renameSingleOnly\": \"Rename is available only when exactly one skill is selected.\",\n \"selectAll\": \"Select All\",\n \"clearSelection\": \"Clear\",\n \"downloadSkillPool\": \"Download Skill Pool\",\n \"uploadSkillPool\": \"Upload Skill Pool\",\n \"config\": \"Config\",\n \"configPlaceholder\": \"{\\\"KEY\\\": \\\"value\\\"}\",\n \"configInvalidJson\": \"Invalid JSON format\",\n \"configSaved\": \"Config saved\",\n \"emptyStateBadge\": \"New workspaces start empty\",\n \"emptyStateTitle\": \"This agent does not have any skills yet\",\n \"emptyStateText\": \"This is expected. New workspaces do not come with skills automatically. Download skills from the local pool or create one here to equip this agent.\",\n \"emptyStateDownload\": \"Download from Skill Pool\",\n \"emptyStateCreate\": \"Create First Skill\",\n \"loadFailed\": \"Failed to load skills\",\n \"refreshFailed\": \"Failed to refresh skills\",\n \"createdSuccessfully\": \"Created successfully\",\n \"saveFailed\": \"Failed to save\",\n \"importFailed\": \"Import failed\",\n \"disabledSuccessfully\": \"Disabled successfully\",\n \"enabledSuccessfully\": \"Enabled successfully\",\n \"operationFailed\": \"Operation failed\",\n \"provideUrl\": \"Please provide a hub skill URL\",\n \"validUrl\": \"Please enter a valid URL starting with http:// or https://\",\n \"importedSkill\": \"Imported skill: {{name}}\",\n \"batchOperation\": \"Batch Operation\",\n \"exitBatch\": \"Exit Batch\",\n \"noSearchResults\": \"No skills found matching your search\",\n \"filterByTag\": \"Filter by tag\",\n \"noTags\": \"No tags available\",\n \"enabledSkills\": \"Enabled Skills\",\n \"disabledSkills\": \"Disabled Skills\",\n \"active\": \"active\"\n },\n \"skillPool\": {\n \"description\": \"Shared local skill pool for built-in skills and workspace uploads. External hub imports such as ClawHub or LobeHub are separate.\",\n \"broadcast\": \"Broadcast to Agents\",\n \"broadcastHint\": \"Broadcast skills to selected agents\",\n \"refreshHint\": \"Refresh skill pool from disk\",\n \"deleteExternalConfirm\": \"This deletes the external skill files at {{path}} from disk.\",\n \"deleteExternalBatchWarning\": \"Selected external skills will be permanently deleted from disk.\",\n \"broadcastTitle\": \"Broadcast {{name}}\",\n \"autoUpdate\": \"Auto Sync\",\n \"autoUpdateOnHint\": \"Auto Sync is on; content changes sync to its agents automatically.\",\n \"autoUpdateEnableHint\": \"Turn on Auto Sync: push content changes to agents that already have this skill.\",\n \"autoUpdateDisableHint\": \"Turn off Auto Sync for this skill.\",\n \"autoUpdateEnabled\": \"Auto Sync enabled for {{name}}\",\n \"autoUpdateDisabled\": \"Auto Sync disabled for {{name}}\",\n \"autoUpdateFailed\": \"Failed to update Auto Sync setting\",\n \"autoUpdateAgentsPlaceholder\": \"All agents that have this skill\",\n \"autoUpdateAgentsHint\": \"Leave empty to sync every agent that already has the skill. Selecting agents pins updates to them (and installs the skill where missing).\",\n \"renameAffectsTitle\": \"Rename in agents too?\",\n \"renameAffectsContent\": \"\\\"{{from}}\\\" is installed in {{count}} agent(s). Rename to \\\"{{to}}\\\"\",\n \"importBuiltin\": \"Update Built-in Skills\",\n \"importBuiltinHint\": \"Update built-in skills to the latest version\",\n \"importBuiltinAlertHint\": \"{{count}} built-in changes need review\",\n \"importBuiltinSuccess\": \"Built-ins imported: {{names}}\",\n \"importBuiltinConflicts\": \"Skipped due to name conflicts: {{names}}\",\n \"importBuiltinNoChanges\": \"No built-in changes to import\",\n \"importBuiltinFailed\": \"Built-in import failed\",\n \"importBuiltinConflictTitle\": \"Builtin conflicts detected\",\n \"importBuiltinConflictContent\": \"The following built-ins differ from the current pool copy. Confirm to overwrite them from source.\",\n \"sourceVersion\": \"Source version\",\n \"currentVersion\": \"Current version\",\n \"builtinLanguage\": \"Language\",\n \"langDefault\": \"Default\",\n \"langDefaultTooltip\": \"Keep each skill in its current language\",\n \"langEn\": \"English\",\n \"langZh\": \"Chinese\",\n \"builtinLanguageChangeTitle\": \"Switch built-in language?\",\n \"builtinLanguageChangeContent\": \"Overwrite the pool copy of \\\"{{name}}\\\" with the {{language}} built-in version?\",\n \"builtinLanguageChangeSuccess\": \"\\\"{{name}}\\\" switched to {{language}}\",\n \"builtinLanguageChangeFailed\": \"Failed to switch built-in language\",\n \"importStatusMissing\": \"Missing in pool\",\n \"importStatusCurrent\": \"Already current\",\n \"importStatusOutdated\": \"Outdated\",\n \"importStatusLanguageSwitchTo\": \"Overwrite with the {{language}} version\",\n \"importStatusConflict\": \"Conflict\",\n \"builtinNoticeSummary\": \"{{count}} built-in changes detected\",\n \"builtinNoticeLineAdded\": \"Added: {{names}}\",\n \"builtinNoticeLineMissing\": \"Missing: {{names}}\",\n \"builtinNoticeLineUpdated\": \"Updated: {{names}}\",\n \"builtinNoticeLineRemoved\": \"Removed: {{names}}\",\n \"zipImport\": \"ZIP Import\",\n \"create\": \"Create\",\n \"edit\": \"Edit\",\n \"delete\": \"Delete\",\n \"descriptionLabel\": \"Description\",\n \"tags\": \"Tags\",\n \"tagsPlaceholder\": \"Select or type to add tags\",\n \"tagTooLong\": \"Tag must be at most {{max}} characters\",\n \"filterByTag\": \"Filter by tag\",\n \"version\": \"Version\",\n \"createTitle\": \"Create Skill Pool Item\",\n \"editTitle\": \"Edit {{name}}\",\n \"skillNamePlaceholder\": \"Skill name\",\n \"contentPlaceholder\": \"SKILL.md content\",\n \"nameConflict\": \"Name conflict detected. Suggested rename applied.\",\n \"deletedFromPool\": \"Deleted from skill pool\",\n \"deleteBuiltinConfirm\": \"This removes the built-in skill from the pool. You can import it again later from source.\",\n \"deleteConfirm\": \"This removes the skill from the shared local skill pool.\",\n \"deleteTitle\": \"Delete {{name}}?\",\n \"batchDeleteTitle\": \"Delete {{count}} pool skills?\",\n \"batchDeleteSuccess\": \"{{count}} pool skills deleted\",\n \"batchDeletePartial\": \"{{deleted}} deleted, {{failed}} failed\",\n \"batchDeleteFailed\": \"Batch delete failed\",\n \"zipImportFailed\": \"ZIP import failed\",\n \"externalHubHint\": \"External hub import is separate from the local Skill Pool.\",\n \"imported\": \"Imported: {{names}}\",\n \"noNewImports\": \"No new skills imported\",\n \"allBuiltin\": \"All Built-in\",\n \"selectWorkspaces\": \"Broadcast to workspace(s)\",\n \"allWorkspaces\": \"All Workspaces\",\n \"optionalRename\": \"Optional rename\",\n \"back\": \"Back\",\n \"skillCount\": \"{{count}} skills\",\n \"skillName\": \"Skill Name\",\n \"status\": \"Status\",\n \"installedFrom\": \"Installed from\",\n \"updateBuiltin\": \"Update\",\n \"updateConfirmTitle\": \"Update {{name}}\",\n \"updateConfirmContent\": \"Update to version {{version}}? This will replace the current pool copy with the latest built-in version.\",\n \"updateSuccess\": \"Updated successfully\",\n \"updateFailed\": \"Update failed\",\n \"broadcastSuccess\": \"Broadcast completed\",\n \"broadcastFailed\": \"Broadcast failed\",\n \"overwriteConfirm\": \"Overwrite existing skills?\",\n \"overwriteTargetsContent\": \"The following existing targets will be overwritten:\",\n \"builtinOverwriteTargetsContent\": \"The following built-in targets have different versions and will be overwritten:\",\n \"renameAndRetry\": \"A skill with this name already exists. Use the suggested name?\",\n \"multiConflictTitle\": \"Multiple Conflicts Detected\",\n \"multiConflictDesc\": \"The following skills already exist. Please resolve conflicts individually and retry:\",\n \"singleConflictRename\": \"\\\"{{name}}\\\" already exists. Rename to \\\"{{suggested}}\\\" to proceed?\",\n \"statusUpToDate\": \"Up-to-date\",\n \"statusSynced\": \"Synced\",\n \"statusOutdated\": \"Outdated\",\n \"statusNotSynced\": \"Not synced\",\n \"statusConflict\": \"Conflict\",\n \"renameEntry\": \"New name for \\\"{{name}}\\\":\",\n \"builtin\": \"Built-in\",\n \"custom\": \"Custom\"\n },\n \"mcp\": {\n \"title\": \"MCP Clients\",\n \"qwenpawManaged\": \"QwenPaw managed\",\n \"qwenpawManagedHint\": \"Shared control-plane configuration injected into supported third-party Agents at runtime.\",\n \"providerManaged\": \"{{provider}} local MCP\",\n \"providerManagedHint\": \"Discovered from the third-party agent's own configuration. These entries are read-only and remain available only to that agent.\",\n \"providerOnly\": \"Third-party agent only\",\n \"readOnly\": \"Read only\",\n \"create\": \"Create Client\",\n \"formatSupport\": \"Supported formats\",\n \"standardFormat\": \"Standard (mcpServers wrapper)\",\n \"directFormat\": \"Direct (key → config object)\",\n \"singleFormat\": \"Single client (flat fields)\",\n \"emptyState\": \"No MCP clients configured yet\",\n \"loadError\": \"Failed to load MCP clients\",\n \"createSuccess\": \"MCP client created successfully\",\n \"createError\": \"Failed to create MCP client\",\n \"updateSuccess\": \"MCP client updated successfully\",\n \"updateError\": \"Failed to update MCP client\",\n \"enableSuccess\": \"MCP client enabled successfully\",\n \"disableSuccess\": \"MCP client disabled successfully\",\n \"toggleError\": \"Failed to toggle MCP client status\",\n \"deleteConfirm\": \"Are you sure you want to delete this MCP client?\",\n \"deleteSuccess\": \"MCP client deleted successfully\",\n \"deleteError\": \"Failed to delete MCP client\",\n \"maskedFieldHint\": \"Sensitive fields are masked with ***. In-place edits on masked values are not supported — replace with the full new value to modify.\",\n \"tools\": \"Tools & Permissions\",\n \"noTools\": \"No tools available from this MCP server\",\n \"toolsLoadError\": \"Failed to load tools from MCP server\",\n \"toolsConnecting\": \"MCP server is still connecting, please try again in a moment\",\n \"toolSchema\": \"Description & Parameters\",\n \"access\": {\n \"title\": \"Access\",\n \"button\": \"Access\",\n \"summary\": \"MCP access policy\",\n \"clientSection\": \"Overall Access\",\n \"toolSection\": \"Tool Access\",\n \"default\": \"Default\",\n \"sourceType\": \"Source\",\n \"sourceValue\": \"Source Value\",\n \"sourceValueAllChannels\": \"All Channels\",\n \"subjectType\": \"Object\",\n \"subjectValue\": \"Object Value\",\n \"effectLabel\": \"Policy\",\n \"source\": {\n \"channel\": \"Channel\"\n },\n \"subjectTypeOption\": {\n \"all\": \"All\",\n \"user\": \"User\"\n },\n \"sourceValuePlaceholder\": {\n \"channel\": \"console / dingtalk\"\n },\n \"subjectValuePlaceholder\": \"e.g. alice\",\n \"subjectValuePlaceholderSelectedSource\": \"user ID in selected source\",\n \"subjectValuePlaceholderScoped\": \"{{source}} user ID\",\n \"subjectValueAll\": \"All\",\n \"recentUserPlaceholder\": \"User ID is scoped to the selected source; type or select again\",\n \"noRecentUsers\": \"No recent users for this source; type a user ID manually\",\n \"ambiguousUserSourceWarning\": \"User IDs are scoped by source. Select a specific channel to avoid cross-channel matches.\",\n \"unknownUserValueWarning\": \"This user ID is not in recent active users. Saving is allowed.\",\n \"validation\": {\n \"missingUserValue\": \"Select a specific source and enter a valid user ID.\",\n \"ambiguousUserSource\": \"User rules must target a specific channel, not all channels.\",\n \"unknownUserValue\": \"This user ID is not in recent active users. Saving is allowed.\"\n },\n \"addRule\": \"Add Rule\",\n \"deleteRule\": \"Delete Rule\",\n \"noClientRules\": \"No rules\",\n \"noRules\": \"No rules, uses default policy\",\n \"inherit\": \"Inherit ({{effect}})\",\n \"stale\": \"Not found\",\n \"disabledTools\": \"This MCP client is disabled; saved access rules can still be edited.\",\n \"discardTitle\": \"Discard unsaved changes?\",\n \"discardContent\": \"Unsaved access policy changes will be lost.\",\n \"loadError\": \"Failed to load MCP access policy\",\n \"saveSuccess\": \"MCP access policy saved\",\n \"saveError\": \"Failed to save MCP access policy\",\n \"unmanagedRules\": \"{{count}} advanced rule(s)\",\n \"effect\": {\n \"allow\": \"Allow\",\n \"ask\": \"Ask\",\n \"deny\": \"Deny\"\n }\n },\n \"tab\": {\n \"json\": \"JSON Import\",\n \"form\": \"Form Mode\"\n },\n \"form\": {\n \"key\": \"Client Key\",\n \"keyPlaceholder\": \"my-mcp-server\",\n \"keyRequired\": \"Client key is required\",\n \"name\": \"Display Name\",\n \"namePlaceholder\": \"My MCP Server\",\n \"nameRequired\": \"Display name is required\",\n \"transport\": \"Transport\",\n \"url\": \"Server URL\",\n \"urlRequired\": \"URL is required for remote MCP servers\",\n \"command\": \"Command\",\n \"commandRequired\": \"Command is required for stdio transport\",\n \"args\": \"Arguments (space or newline separated)\",\n \"description\": \"Description\",\n \"descriptionPlaceholder\": \"Optional description\",\n \"env\": \"Environment Variables\",\n \"envPlaceholder\": \"KEY=VALUE (one per line)\"\n },\n \"oauth\": {\n \"enableOAuth\": \"Use OAuth Authorization\",\n \"manage\": \"OAuth\",\n \"authorized\": \"Authorized\",\n \"notAuthorized\": \"Not Authorized\",\n \"expired\": \"Token Expired\",\n \"failed\": \"Authorization Failed\",\n \"waiting\": \"Waiting for authorization...\",\n \"authorize\": \"Authorize\",\n \"reauthorize\": \"Re-authorize\",\n \"revoke\": \"Revoke\",\n \"startFailed\": \"Failed to start OAuth flow\",\n \"authFailed\": \"OAuth authorization failed\",\n \"popupBlocked\": \"Popup was blocked by the browser. Please allow popups and try again.\",\n \"windowClosed\": \"Authorization window was closed. Please try again.\",\n \"noUrl\": \"Please enter a server URL first\",\n \"noClientKey\": \"Please create the client first\",\n \"showAdvanced\": \"Advanced options\",\n \"hideAdvanced\": \"Hide advanced options\",\n \"clientId\": \"Client ID (optional)\",\n \"clientIdPlaceholder\": \"Leave empty to use Dynamic Client Registration\",\n \"scope\": \"Scope (optional)\",\n \"scopePlaceholder\": \"openid profile email\",\n \"authEndpoint\": \"Authorization Endpoint (override)\",\n \"tokenEndpoint\": \"Token Endpoint (override)\",\n \"endpointHint\": \"Leave empty to auto-discover from the MCP server. Fill in to skip discovery.\"\n }\n },\n \"heartbeat\": {\n \"title\": \"Heartbeat\",\n \"description\": \"Run HEARTBEAT.md at a fixed interval for self-checks. By default runs silently without affecting current conversations, or optionally send replies to the last chat channel.\",\n \"enabled\": \"Enable heartbeat\",\n \"every\": \"Interval\",\n \"everyRequired\": \"Required\",\n \"everyMin\": \"Must be at least 1\",\n \"timeoutSeconds\": \"Execution timeout (seconds)\",\n \"timeoutRequired\": \"Required\",\n \"timeoutMin\": \"Must be at least 1\",\n \"timeoutMax\": \"Must be at most 3600\",\n \"unitMinutes\": \"Minutes\",\n \"unitHours\": \"Hours\",\n \"target\": \"Reply target\",\n \"targetMain\": \"Silent mode (default, no channel output)\",\n \"targetLast\": \"Send to last chat channel\",\n \"targetInbox\": \"Send to inbox\",\n \"activeHours\": \"Active hours (optional)\",\n \"activeStart\": \"Start time\",\n \"activeEnd\": \"End time\",\n \"loadFailed\": \"Failed to load heartbeat config\",\n \"saveSuccess\": \"Saved successfully; heartbeat hot-reloaded\",\n \"saveFailed\": \"Failed to save heartbeat config\",\n \"runNow\": \"Run Now\",\n \"runNowSuccess\": \"Heartbeat triggered. Check Inbox or target channel shortly.\",\n \"runNowFailed\": \"Failed to trigger heartbeat run\"\n },\n \"cronJobs\": {\n \"title\": \"Cron Jobs\",\n \"description\": \"Create and manage scheduled tasks that automatically execute at specified times. \",\n \"createJob\": \"Create Job\",\n \"createFromTemplate\": \"Create From Template\",\n \"editJob\": \"Edit Job\",\n \"templateModalTitle\": \"Choose a Task Template\",\n \"templateModalDescription\": \"Pick a built-in template and tweak key parameters in the drawer before saving.\",\n \"useTemplate\": \"Use Template\",\n \"templateTagPersonal\": \"Personal\",\n \"templateTagTeam\": \"Team\",\n \"templateTagReminder\": \"Reminder\",\n \"templateTagCalendar\": \"Calendar\",\n \"templateTagCalendarRecommended\": \"Calendar Recommended\",\n \"confirmDelete\": \"Confirm Delete\",\n \"deleteConfirm\": \"Are you sure you want to delete this Cron Job?\",\n \"okText\": \"OK\",\n \"cancelText\": \"Cancel\",\n \"deleteText\": \"Delete\",\n \"id\": \"Job ID\",\n \"name\": \"Job Name\",\n \"enabled\": \"Status\",\n \"saveResultToInbox\": \"Save result to Inbox\",\n \"saveResultToInboxTooltip\": \"When enabled, successful executions with successful delivery are stored in Inbox. Delivery failures are always written to Inbox as fallback.\",\n \"scheduleType\": \"Schedule Type\",\n \"scheduleFilterAll\": \"All Schedule Types\",\n \"scheduleTypeRecurring\": \"Recurring\",\n \"scheduleTypeOnce\": \"Scheduled\",\n \"scheduleCron\": \"Schedule (Cron)\",\n \"scheduleCronLabel\": \"Schedule (Cron)\",\n \"onceRunAt\": \"Run At\",\n \"repeatEnabled\": \"Repeat\",\n \"repeatEnabledTooltip\": \"Enable recurring schedule from this start time\",\n \"repeatFrequency\": \"Repeat Frequency\",\n \"repeatEveryDays\": \"Repeat Every (days)\",\n \"repeatEveryPrefix\": \"Every\",\n \"repeatEverySuffix\": \"day(s)\",\n \"repeatEndType\": \"End Repeat\",\n \"repeatEndNever\": \"Never\",\n \"repeatEndUntil\": \"End on date\",\n \"repeatEndCount\": \"Limited count\",\n \"repeatUntil\": \"End Date\",\n \"repeatCount\": \"Run Count\",\n \"scheduleTimezone\": \"Timezone\",\n \"cronType\": \"Frequency\",\n \"cronTypeHourly\": \"Hourly\",\n \"cronTypeDaily\": \"Daily\",\n \"cronTypeWeekly\": \"Weekly\",\n \"cronTypeCustom\": \"Custom\",\n \"cronTime\": \"Time\",\n \"cronDaysOfWeek\": \"Days of Week\",\n \"cronDayMon\": \"Monday\",\n \"cronDayTue\": \"Tuesday\",\n \"cronDayWed\": \"Wednesday\",\n \"cronDayThu\": \"Thursday\",\n \"cronDayFri\": \"Friday\",\n \"cronDaySat\": \"Saturday\",\n \"cronDaySun\": \"Sunday\",\n \"cronCustomExpression\": \"Cron Expression\",\n \"taskText\": \"Message content\",\n \"action\": \"Action\",\n \"disable\": \"Disable\",\n \"executeNow\": \"Execute Now\",\n \"edit\": \"Edit\",\n \"delete\": \"Delete\",\n \"totalItems\": \"Total {{count}} items\",\n \"perPage\": \"/ page\",\n \"pleaseInputId\": \"Please input job ID\",\n \"pleaseInputName\": \"Please input job name\",\n \"pleaseInputCron\": \"Please input cron expression\",\n \"pleaseSelectScheduleType\": \"Please select schedule type\",\n \"pleaseInputRunAt\": \"Please select run time\",\n \"pleaseInputRepeatEveryDays\": \"Please enter repeat interval (days)\",\n \"pleaseSelectRepeatEndType\": \"Please select end condition\",\n \"pleaseInputRepeatUntil\": \"Please select end date\",\n \"pleaseInputRepeatCount\": \"Please enter run count\",\n \"validation\": {\n \"cronRequired\": \"Cron expression is required for recurring schedules.\",\n \"runAtRequired\": \"Execution time is required for one-time schedules.\",\n \"repeatUntilRequired\": \"Please select an end date when using \\\"End on date\\\".\",\n \"repeatCountRequired\": \"Please provide a run count when using \\\"Limited count\\\".\",\n \"repeatUntilAfterRunAt\": \"The end date must be later than the execution time. Please choose an end date after the execution time.\",\n \"textRequired\": \"Message content is required when task type is text.\",\n \"requestRequired\": \"Request content is required when task type is agent.\",\n \"invalidCronExpression\": \"Invalid cron expression. Please use a 5-field cron format.\"\n },\n \"pleaseSelectTaskType\": \"Please select task type\",\n \"pleaseInputRequest\": \"Please input request content\",\n \"pleaseInputMessageContent\": \"Please enter message content\",\n \"pleaseInputChannel\": \"Please input target channel\",\n \"pleaseInputUserId\": \"Please input target user ID\",\n \"pleaseInputSessionId\": \"Please input target session ID\",\n \"jobIdPlaceholder\": \"e.g., daily-report-job\",\n \"jobNamePlaceholder\": \"e.g., Daily Morning Report\",\n \"selectTimezone\": \"Select timezone\",\n \"taskDescriptionPlaceholder\": \"For simple text tasks, enter the message body to send...\",\n \"invalidJsonFormat\": \"Invalid JSON format\",\n \"jsonFormatRequired\": \"JSON format required\",\n \"taskType\": \"Task Type\",\n \"text\": \"Message content\",\n \"requestInput\": \"Request Content\",\n \"requestSessionId\": \"Request Session ID\",\n \"requestUserId\": \"Request User ID\",\n \"dispatchChannel\": \"Target Channel\",\n \"dispatchTargetUserId\": \"Target User ID\",\n \"dispatchTargetSessionId\": \"Target Session ID\",\n \"dispatchMode\": \"Delivery Mode\",\n \"silentDelivery\": \"Silent delivery\",\n \"runtimeShareSession\": \"Share Session\",\n \"runtimeMaxConcurrency\": \"Max Concurrency\",\n \"runtimeTimeoutSeconds\": \"Timeout (seconds)\",\n \"runtimeMisfireGraceSeconds\": \"Misfire Grace (seconds)\",\n \"runtimeToolSafety\": \"Tool Safety Check\",\n \"idTooltip\": \"System-generated, non-editable, unique UUID assigned at task creation.\",\n \"nameTooltip\": \"A friendly name to help you identify this job.\",\n \"cronTooltip\": \"Define when the task should run\",\n \"cronExample\": \"Common examples: '0 9 * * *' = 9 AM daily | '*/30 * * * *' = every 30 min | '0 */2 * * *' = every 2 hours | '0 0 * * 0' = Sunday midnight\",\n \"cronHelper\": \"New to Cron expressions?\",\n \"cronHelperLink\": \"Use online generator\",\n \"timezoneTooltip\": \"Timezone for the cron schedule. Default: UTC\",\n \"taskTypeTooltip\": \"Choose 'text' for simple message tasks, or 'agent' for complex agent workflows.\",\n \"textTooltip\": \"Simple message task: this is the actual message body. Required when task type is 'text'.\",\n \"requestInputTooltip\": \"Message content in JSON format. This is what the agent will receive and process. Required when task type is 'agent'.\",\n \"requestInputExample\": \"Format: [{\\\"role\\\":\\\"user\\\",\\\"content\\\":[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Your message here\\\"}]}]\",\n \"requestSessionIdTooltip\": \"Session ID for the request context. Use 'default' if unsure.\",\n \"requestUserIdTooltip\": \"User ID that initiates the request. Use 'system' for automated tasks.\",\n \"dispatchChannelTooltip\": \"Target channel where the response will be sent (e.g., 'console', 'discord', 'imessage').\",\n \"dispatchTargetUserIdTooltip\": \"User ID who will receive the response in the target channel.\",\n \"dispatchTargetSessionIdTooltip\": \"Session ID where the response will be delivered in the target channel.\",\n \"dispatchModeTooltip\": \"Choose 'stream' for real-time responses or 'final' for complete responses only.\",\n \"silentDeliveryTooltip\": \"Run the agent task and keep its session and trace, but do not send results to the channel.\",\n \"shareSessionTooltip\": \"When enabled, shares session with target user. When disabled, each run creates isolated context with unique run ID. Suitable for independent tasks without memory. Default: enabled\",\n \"maxConcurrencyTooltip\": \"Maximum number of this job that can run simultaneously. Default: 1\",\n \"timeoutSecondsTooltip\": \"Maximum execution time in seconds. Job will be terminated if exceeded.\",\n \"misfireGraceSecondsTooltip\": \"Grace period for missed executions. If a job misses its scheduled time by more than this, it won't run.\",\n \"toolSafetyTooltip\": \"When enabled, risky tool calls require user approval (may block unattended cron jobs). When disabled, all tools execute without approval prompts, suitable for trusted automated tasks. Default: disabled\",\n \"executeNowTitle\": \"Execute Task Now\",\n \"executeNowContent\": \"Are you sure you want to execute \\\"{{name}}\\\" now?\",\n \"executeNowConfirm\": \"Execute Now\",\n \"listView\": \"List View\",\n \"calendarView\": \"Calendar View\",\n \"calendarEmptyHint\": \"No scheduled jobs yet. Create scheduled jobs to see them on the calendar.\",\n \"calendarMoreItems\": \"+{{count}} more\",\n \"calendarDayListTitle\": \"Scheduled jobs on {{date}}\",\n \"executionHistory\": \"History\",\n \"historyTitle\": \"History - {{name}}\",\n \"historyEmpty\": \"No execution records yet.\",\n \"historyStatusSuccess\": \"Success\",\n \"historyStatusFailed\": \"Failed\",\n \"historyStatusRunning\": \"Running\",\n \"historyStatusCancelled\": \"Cancelled\",\n \"historyTriggerScheduled\": \"Triggered by schedule\",\n \"historyTriggerManual\": \"Triggered manually\",\n \"historyExpand\": \"Expand\",\n \"historyCollapse\": \"Collapse\",\n \"templates\": {\n \"dailyTechNewsBrief\": {\n \"title\": \"Daily Tech News Brief\",\n \"description\": \"At 09:30 on weekdays, compile trending tech headlines into a short digest.\",\n \"frequency\": \"Weekdays 09:30 (30 9 * * 1-5)\"\n },\n \"dailyEveningReflection\": {\n \"title\": \"Daily Evening Reflection\",\n \"description\": \"At 20:00 on weekdays, prompt a quick reflection on work completed today.\",\n \"frequency\": \"Weekdays 20:00 (0 20 * * 1-5)\"\n },\n \"weekendRelaxationReminder\": {\n \"title\": \"Weekend Relaxation Reminder\",\n \"description\": \"On weekend mornings, push curated hot movie picks for a lighter break.\",\n \"frequency\": \"Weekends 10:00 (0 10 * * 6,0)\"\n },\n \"pomodoroBreakReminder\": {\n \"title\": \"Pomodoro Break Reminder\",\n \"description\": \"During work hours, remind every 25 minutes to hydrate or rest your eyes.\",\n \"frequency\": \"Weekdays 09:00-17:59 every 25 min (*/25 9-17 * * 1-5)\"\n },\n \"petCareReminder\": {\n \"title\": \"Pet Deworming/Vaccine Reminder\",\n \"description\": \"On the 15th each month at night, remind pet deworming or vaccine follow-up.\",\n \"frequency\": \"Monthly on the 15th at 20:00 (0 20 15 * *)\"\n },\n \"onceTextBirthdayReminder\": {\n \"title\": \"Birthday Reminder\",\n \"description\": \"Remind that xx has a birthday on Jan 1.\",\n \"frequency\": \"Run at: 2026-01-01 09:00\"\n },\n \"onceAgentBusinessTripPrep\": {\n \"title\": \"Business Trip Prep\",\n \"description\": \"Check destination weather and provide trip preparation suggestions.\",\n \"frequency\": \"Run at: 2026-01-01 20:00\"\n },\n \"repeatCountTextMedicineReminder\": {\n \"title\": \"Medication Reminder (14 runs)\",\n \"description\": \"Remind to take medicine on time for a 14-run course.\",\n \"frequency\": \"First run: 2026-01-01 09:00 | Every 1 day | 14 runs\"\n },\n \"repeatCountAgentDietPlan\": {\n \"title\": \"14-Day Meal Plan\",\n \"description\": \"Generate and send daily meal suggestions for 14 days.\",\n \"frequency\": \"First run: 2026-01-01 08:00 | Every 1 day | 14 runs\"\n },\n \"repeatUntilTextWeeklyMeeting\": {\n \"title\": \"Weekly Meeting Reminder (2 months)\",\n \"description\": \"Send a weekly meeting reminder until the configured deadline.\",\n \"frequency\": \"First run: 2026-01-02 08:45 | Every 7 days | Until 2026-03-01\"\n },\n \"repeatUntilAgentWeeklySummary\": {\n \"title\": \"Pre-Meeting Weekly Summary\",\n \"description\": \"Before each weekly meeting, summarize the last week's memory.\",\n \"frequency\": \"First run: 2026-01-02 08:30 | Every 7 days | Until 2026-03-01\"\n }\n }\n },\n \"channels\": {\n \"title\": \"Channels\",\n \"description\": \"Manage and configure message channels\",\n \"loading\": \"Loading channels...\",\n \"configSaved\": \"Configuration saved successfully\",\n \"configFailed\": \"Failed to save configuration\",\n \"channelType\": \"Channel Type\",\n \"status\": \"Status\",\n \"totalItems\": \"Total {{count}} items\",\n \"botPrefix\": \"Bot Prefix\",\n \"showToolCalls\": \"Show Tool Call Information\",\n \"showToolCallsTooltip\": \"Show tool call information to users (turn off to hide)\",\n \"showToolResults\": \"Show Tool Result Information\",\n \"showToolResultsTooltip\": \"Show tool result information to users (turn off to hide)\",\n \"toolCallMaxLength\": \"Tool Call Maximum Display Length\",\n \"toolResultMaxLength\": \"Tool Result Maximum Display Length\",\n \"toolMaxLengthTooltip\": \"Set to 0 for unlimited length\",\n \"onebotMediaBase64\": \"Send Local Media as Base64\",\n \"onebotMediaBase64Tooltip\": \"Choose how media files are sent. When disabled, the file path is sent; when enabled, the file is encoded as Base64. Remote URLs remain unchanged.\",\n \"onebotMediaBase64MaxMb\": \"Base64 File Size Limit\",\n \"onebotMediaBase64MaxMbTooltip\": \"Media files larger than this limit are sent using their original file path.\",\n \"onebotMediaBase64MaxMbRequired\": \"Please enter a Base64 file size limit\",\n \"onebotMediaBase64MaxMbMin\": \"The Base64 file size limit must be at least 1 MB\",\n \"showThinking\": \"Show Thinking\",\n \"showThinkingTooltip\": \"Show model thinking/reasoning content to users (turn off to hide)\",\n \"noTextDebounce\": \"No-text Debounce\",\n \"noTextDebounceTooltip\": \"When enabled, media-only messages (image/video/file) are buffered until a text message arrives, then merged. Disable to process all messages immediately. Voice messages are always processed immediately.\",\n \"streamingEnabled\": \"Streaming Output\",\n \"streamingEnabledDingtalkHint\": \"Only effective when message type is Card\",\n \"streamingEnabledFeishuHint\": \"Requires enabling cardkit:card:write permission in the Feishu Open Platform permission management page\",\n \"ackMessage\": \"Acknowledgment Message\",\n \"ackMessageTooltip\": \"Send an instant reply when a message is received, before agent processing begins. Leave empty to disable.\",\n \"ackMessagePlaceholder\": \"⏳ Processing...\",\n \"notSet\": \"Not set\",\n \"clickCardToEdit\": \"Click card to edit\",\n \"settings\": \"Settings\",\n \"channelSettings\": \"Channel Settings\",\n \"pleaseInputDbPath\": \"Please input DB path\",\n \"pleaseInputPollInterval\": \"Please input poll interval\",\n \"discordBotToken\": \"Discord bot token\",\n \"acceptBotMessages\": \"Accept Bot Messages\",\n \"acceptBotMessagesTooltip\": \"When enabled, messages from other bots will be accepted\",\n \"httpProxyPlaceholder\": \"http://127.0.0.1:18118\",\n \"httpProxyAuthPlaceholder\": \"user:password\",\n \"dbPathPlaceholder\": \"~/Library/Messages/chat.db\",\n \"botPrefixPlaceholder\": \"@bot\",\n \"dmPolicy\": \"DM Policy\",\n \"dmPolicyTooltip\": \"Control who can interact with the bot via direct message. Open: everyone; Allowlist: authorized users only\",\n \"groupPolicy\": \"Group Policy\",\n \"groupPolicyTooltip\": \"Control who can interact with the bot in group chats. Open: everyone; Allowlist: authorized users only\",\n \"requireMention\": \"Require @Mention\",\n \"requireMentionTooltip\": \"When enabled, bot only responds in group chats when explicitly @mentioned\",\n \"policyOpen\": \"Open\",\n \"policyAllowlist\": \"Allowlist\",\n \"allowFrom\": \"Allowlist Users\",\n \"allowFromTooltip\": \"List of user IDs allowed to interact with the bot. Enable allowlist first, then DM the bot to see your ID in the deny message\",\n \"allowFromPlaceholder\": \"Enter user ID and press Enter to add\",\n \"denyMessage\": \"Access Denied Message\",\n \"denyMessageTooltip\": \"Custom message shown to users when access is denied\",\n \"twilioAccountSid\": \"Twilio Account SID\",\n \"twilioAuthToken\": \"Twilio Auth Token\",\n \"phoneNumber\": \"Phone Number\",\n \"phoneNumberSid\": \"Phone Number SID\",\n \"phoneNumberSidHelp\": \"Found under Phone Numbers → Active Numbers in the Twilio Console.\",\n \"ttsProvider\": \"TTS Provider\",\n \"ttsVoice\": \"TTS Voice\",\n \"sttProvider\": \"STT Provider\",\n \"language\": \"Language\",\n \"welcomeGreeting\": \"Welcome Greeting\",\n \"welcomeText\": \"Welcome Message\",\n \"welcomeTextTooltip\": \"Message automatically sent when a user enters a single chat session with the bot for the first time that day\",\n \"welcomeTextPlaceholder\": \"e.g. Hello! I'm QwenPaw. How can I help you?\",\n \"loginWeCom\": \"Get WeCom QR Code\",\n \"wecomSetupGuide\": \"When creating a bot via QR code, please select \\\"Skip Authorization\\\" when asked to authorize bot capabilities. If you select \\\"Confirm Authorization\\\", users other than the bot creator may not be able to interact with the bot.\",\n \"wecomScanAuth\": \"Scan to Authorize\",\n \"wecomAuthHint\": \"Scan the QR code above with WeCom to authorize. Bot ID and Secret will be filled in automatically.\",\n \"wecomAuthSuccess\": \"WeCom bot authorized. Bot ID and Secret have been filled in.\",\n \"wecomQrcodeFailed\": \"Failed to get WeCom QR code. Please try again.\",\n \"slackBotToken\": \"Bot Token\",\n \"slackBotTokenTooltip\": \"Slack Bot User OAuth Token, starts with xoxb-\",\n \"slackAppToken\": \"App Token\",\n \"slackAppTokenTooltip\": \"Slack App-Level Token for Socket Mode, starts with xapp-\",\n \"slackProxyTooltip\": \"HTTP proxy URL for connecting to Slack API\",\n \"dingtalkSetupGuide\": \"Scan the QR code with DingTalk to create a bot instantly. A DingTalk app will be created automatically and Client ID / Client Secret will be filled in.\",\n \"dingtalkScanAuth\": \"Scan to Create Bot\",\n \"dingtalkGetQrcode\": \"Get DingTalk QR Code\",\n \"dingtalkScanHint\": \"Scan the QR code above with DingTalk. Client ID and Client Secret will be filled in automatically after authorization.\",\n \"dingtalkAuthSuccess\": \"DingTalk bot created. Client ID and Client Secret have been filled in.\",\n \"dingtalkQrcodeFailed\": \"Failed to get DingTalk QR code. Please try again.\",\n \"dingtalkQrcodeExpired\": \"QR code expired, please get a new one.\",\n \"qqSetupGuide\": \"Use QQ to scan the QR code to authorize the bot. After scanning, the APP ID and Client Secret will be automatically retrieved and filled in.\",\n \"qqScanAuth\": \"Scan the QR code to authorize the bot\",\n \"qqGetQrcode\": \"Get QQ QR code\",\n \"qqScanHint\": \"Please use QQ to scan the QR code above. Once authorization is complete, the APP ID and Client Secret will be automatically filled in.\",\n \"qqAuthSuccess\": \"QQ bot authorization successful. The APP ID and Client Secret have been automatically filled in.\",\n \"qqQrcodeFailed\": \"Failed to retrieve the QQ QR code; please try again later\",\n \"qqQrcodeExpired\": \"The QR code has expired; please retrieve a new one\",\n \"atSenderOnReply\": \"@ Sender on Reply\",\n \"atSenderOnReplyTooltip\": \"When enabled, the bot will @mention the sender in its first reply message in group chats. Note: In card mode, @mention only works for enterprise internal members.\",\n \"dingtalkEndpoint\": \"API Endpoint\",\n \"dingtalkEndpointTooltip\": \"Custom API endpoint for private DingTalk deployments. Leave empty to use the official default.\",\n \"wecomCancelled\": \"Authorization cancelled\",\n \"wecomAuthFailed\": \"Authorization failed: {{msg}}\",\n \"voiceSetupGuide\": \"Set up a Twilio account, purchase a phone number, then enter your credentials below. Find your Account SID and Auth Token on the Twilio Console dashboard. The Phone Number SID is listed under Phone Numbers → Active Numbers.\",\n \"voiceSetupLink\": \"Open Twilio Console\",\n \"sipSetupGuide\": \"Configure a SIP registrar (e.g. Asterisk, FreeSWITCH) and enter your SIP credentials below. In Dev mode, pyVoIP handles SIP/RTP locally. In Production mode, LiveKit SIP Server is used.\",\n \"sipMode\": \"SIP Mode\",\n \"sipModeTooltip\": \"Dev: pure-Python pyVoIP for local testing. Production: LiveKit SIP Server for high concurrency.\",\n \"sipServer\": \"SIP Server\",\n \"sipServerPlaceholder\": \"Leave empty to use built-in registrar\",\n \"sipServerPlaceholderLivekit\": \"Not needed for LiveKit mode\",\n \"sipUsername\": \"SIP Username\",\n \"sipPassword\": \"SIP Password\",\n \"sipPort\": \"SIP Port\",\n \"sipTransport\": \"Transport\",\n \"sipDashscopeApiKey\": \"DashScope API Key\",\n \"sipDashscopeApiKeyTooltip\": \"API key for Aliyun DashScope STT/TTS. If empty, falls back to the DASHSCOPE_API_KEY environment variable.\",\n \"livekitUrl\": \"LiveKit URL\",\n \"livekitApiKey\": \"LiveKit API Key\",\n \"livekitApiSecret\": \"LiveKit API Secret\",\n \"livekitSipTrunkId\": \"LiveKit SIP Trunk ID\",\n \"livekitRoomName\": \"LiveKit Room Name\",\n \"livekitRoomNameTooltip\": \"The LiveKit room name to join and wait for SIP calls. Must match the room name in your SIP Dispatch Rule.\",\n \"xiaoyiSetupGuide\": \"Please create an agent on Huawei Developer Platform and get AK/SK and Agent ID. AK/SK can be found in the credential management page.\",\n \"filterAll\": \"All\",\n \"builtin\": \"Built-in\",\n \"custom\": \"Custom\",\n \"feishuRegion\": \"Region\",\n \"feishuRegionTooltip\": \"Choose Feishu for China or Lark for International\",\n \"feishuChina\": \"Feishu (China)\",\n \"feishuInternational\": \"Lark (International)\",\n \"feishuScanGuide\": \"Scan the QR code with Feishu to create a bot instantly. A Feishu app will be created automatically and App ID / App Secret will be filled in.\",\n \"feishuScanLogin\": \"Scan to Create Bot\",\n \"feishuGetQrcode\": \"Get Feishu QR Code\",\n \"feishuScanHint\": \"Scan the QR code above with Feishu. App ID and App Secret will be filled in automatically after authorization.\",\n \"feishuAuthSuccess\": \"Feishu bot created. App ID and App Secret have been filled in.\",\n \"feishuQrcodeExpired\": \"QR code expired, please get a new one.\",\n \"feishuQrcodeFailed\": \"Failed to get Feishu QR code. Please try again.\",\n \"wechatSetupGuide\": \"WeChat personal account Bot (iLink protocol). On first start without a Bot Token, a QR code login URL will be printed; scan to authorize. The token is saved locally for future use.\",\n \"wechatContextTokenLimit\": \"WeChat iLink platform limitation: each user message's context_token allows a maximum of 10 reply messages. This is a hard limit enforced by the platform. It is recommended to disable thinking and tool output, or enable Message Merge to avoid exceeding the limit and causing message send failures.\",\n \"wechatBotToken\": \"Bot Token\",\n \"wechatBotTokenTooltip\": \"Bearer token obtained after QR code login. Leave empty to trigger QR code login on startup.\",\n \"wechatBotTokenPlaceholder\": \"Auto-filled after QR login, or paste manually\",\n \"wechatBotTokenFile\": \"Token File Path\",\n \"wechatBotTokenFileTooltip\": \"Path to persist the bot token. Defaults to ~/.qwenpaw/wechat_bot_token\",\n \"wechatMediaDir\": \"Media Directory\",\n \"wechatMessageMerge\": \"Message Merge\",\n \"wechatMessageMergeTooltip\": \"Merge multiple reply messages into fewer messages to avoid hitting the WeChat 10-message limit per context_token. When enabled with delay 0, all text replies are merged into a single message sent at the end. A positive delay (in ms) merges adjacent messages within that time window.\",\n \"wechatMessageMergeDelayMs\": \"Merge Delay (ms)\",\n \"wechatMessageMergeDelayMsTooltip\": \"Time window for adjacent message merging. Set to 0 to merge all messages into one final reply. A positive value (e.g. 1000) will buffer messages for that duration before sending.\",\n \"wechatMessageMergeDelayMsValidation\": \"Please enter a valid non-negative integer\",\n \"wechatScanLogin\": \"Scan to Login\",\n \"wechatGetQrcode\": \"Get Login QR Code\",\n \"wechatScanHint\": \"Scan the QR code with WeChat, then click Save\",\n \"wechatQrcodeFailed\": \"Failed to fetch QR code, please try again\",\n \"wechatLoginSuccess\": \"WeChat login successful, token has been filled in\",\n \"wechatQrcodeExpired\": \"QR code expired, please get a new one\",\n \"shareSessionInGroup\": \"Share Context in Group\",\n \"shareSessionInGroupTooltip\": \"When enabled, all group members share the same conversation context. When disabled, each member has their own independent context.\",\n \"accessControlEnabled\": \"Access Control\",\n \"accessControlEnabledTooltip\": \"When enabled, only whitelisted users can interact with this bot. Unknown users will be prompted with their ID and added to the pending approval list.\",\n \"accessControlDm\": \"DM Access Control\",\n \"accessControlDmTooltip\": \"When enabled, only whitelisted users can interact with the bot in direct messages\",\n \"accessControlGroup\": \"Group Access Control\",\n \"accessControlGroupTooltip\": \"When enabled, only whitelisted users can interact with the bot in group chats\",\n \"dmDisabled\": \"Disable DM\",\n \"dmDisabledTooltip\": \"When enabled, the bot will completely ignore all direct messages\",\n \"groupDisabled\": \"Disable Group\",\n \"groupDisabledTooltip\": \"When enabled, the bot will completely ignore all group messages\",\n \"manageAccessControl\": \"Access Control\",\n \"pendingApprovals\": \"Pending Approvals\",\n \"whitelist\": \"Whitelist\",\n \"blacklist\": \"Blacklist\",\n \"addUser\": \"Add User\",\n \"addUserPlaceholder\": \"Enter user ID\",\n \"userId\": \"User ID\",\n \"username\": \"Username\",\n \"channel\": \"Channel\",\n \"firstMessage\": \"First Message\",\n \"time\": \"Time\",\n \"actions\": \"Actions\",\n \"approve\": \"Whitelist\",\n \"deny\": \"Blacklist\",\n \"dismiss\": \"Dismiss\",\n \"noWhitelistUsers\": \"No whitelist users\",\n \"noBlacklistUsers\": \"No blacklist users\",\n \"noPendingApprovals\": \"No pending approvals\",\n \"userAdded\": \"User added successfully\",\n \"userRemoved\": \"User deleted successfully\",\n \"approveSuccess\": \"User added to whitelist\",\n \"denySuccess\": \"User added to blacklist\",\n \"dismissSuccess\": \"Pending entry dismissed\",\n \"operationFailed\": \"Operation failed\",\n \"remark\": \"Remark\",\n \"usernamePlaceholder\": \"Enter username (optional)\",\n \"remarkPlaceholder\": \"Enter a remark (optional)\",\n \"filterByChannel\": \"Filter by channel\",\n \"selectToOperate\": \"Select items to operate\",\n \"selectedCount\": \"{{count}} selected\",\n \"batchApprove\": \"Approve\",\n \"batchDeny\": \"Blacklist\",\n \"batchDismiss\": \"Dismiss\",\n \"batchApproveConfirm\": \"Approve {{count}} selected users?\",\n \"batchDenyConfirm\": \"Blacklist {{count}} selected users?\",\n \"batchDismissConfirm\": \"Dismiss {{count}} selected users?\",\n \"batchSuccess\": \"{{count}} users processed successfully\",\n \"batchRemove\": \"Delete\",\n \"batchRemoveConfirm\": \"Delete {{count}} selected users?\",\n \"enabledSection\": \"Enabled\",\n \"enabledCount\": \"{{count}}\",\n \"availableSection\": \"Available\",\n \"enableAction\": \"Enable\",\n \"noEnabledChannels\": \"No enabled channels\",\n \"goEnableChannels\": \"Enable channels\"\n },\n \"sessions\": {\n \"title\": \"Sessions\",\n \"description\": \"View and manage active chat sessions\",\n \"loading\": \"Loading sessions...\",\n \"confirmDelete\": \"Confirm Delete\",\n \"deleteConfirm\": \"Are you sure you want to delete this session?\",\n \"batchDeleteConfirm\": \"Are you sure you want to delete selected {{count}} sessions?\",\n \"deleteSuccess\": \"Session deleted successfully\",\n \"deleteFailed\": \"Failed to delete session\",\n \"batchDeleteSuccess\": \"Successfully deleted {{count}} session(s)\",\n \"batchDeleteFailed\": \"Failed to batch delete sessions\",\n \"saveSuccess\": \"Session saved successfully\",\n \"saveFailed\": \"Failed to save session\",\n \"batchDeleteButton\": \"Batch Delete\",\n \"filterTitle\": \"Filter by Title\",\n \"filterUserId\": \"Filter by User ID\",\n \"filterChannel\": \"Filter by Channel\",\n \"allChannels\": \"All Channels\",\n \"totalItems\": \"Total {{count}} items\",\n \"selectedItems\": \"Selected {{count}} items\",\n \"editSession\": \"Edit Session\",\n \"pleaseInputName\": \"Please input session name\",\n \"sessionNamePlaceholder\": \"Session name\",\n \"activeTab\": \"Active\",\n \"archivedTab\": \"Archived\",\n \"archivedAtColumn\": \"Archived At\",\n \"archive\": {\n \"action\": \"Archive\",\n \"unaction\": \"Unarchive\",\n \"batchAction\": \"Batch Archive\",\n \"success\": \"Chat archived\",\n \"successHint\": \"Chat archived. View in Control > Sessions > Archived.\",\n \"failed\": \"Failed to archive\",\n \"unarchiveSuccess\": \"Chat unarchived\",\n \"unarchiveFailed\": \"Failed to unarchive\",\n \"batchSuccess\": \"{{count}} chat(s) archived\",\n \"batchFailed\": \"Failed to batch archive\",\n \"batchUnaction\": \"Batch Unarchive\",\n \"batchUnarchiveSuccess\": \"{{count}} chat(s) unarchived\",\n \"batchUnarchiveFailed\": \"Failed to batch unarchive\",\n \"groupLabel\": \"Archived ({{count}})\",\n \"modalTitle\": \"Archived Chats\",\n \"unarchiveAll\": \"Unarchive All\",\n \"noArchived\": \"No archived chats\"\n }\n },\n \"environments\": {\n \"parent\": \"Settings\",\n \"environments\": \"Environment Variables\",\n \"title\": \"Environment Variables\",\n \"description\": \"Configure key-value environment variables for agents and skills.\",\n \"key\": \"Key\",\n \"value\": \"Value\",\n \"variableNamePlaceholder\": \"VARIABLE_NAME\",\n \"valuePlaceholder\": \"value\",\n \"insertRowBelow\": \"Insert row below\",\n \"deleteRow\": \"Delete row\",\n \"deleteVariable\": \"Delete Variable\",\n \"deleteConfirm\": \"Delete \\\"{{name}}\\\"?\",\n \"deleteSelected\": \"Delete Selected\",\n \"deleteSelectedConfirm\": \"Delete {{label}}?\",\n \"keyRequired\": \"Key is required\",\n \"invalidKeyFormat\": \"Invalid key format\",\n \"duplicateKey\": \"Duplicate key\",\n \"saveSuccess\": \"Environment variables saved\",\n \"saveFailed\": \"Failed to save\",\n \"deleteSuccess\": \"\\\"{{name}}\\\" deleted\",\n \"deleteFailed\": \"Failed to delete\",\n \"noVariables\": \"No environment variables configured yet.\",\n \"addVariable\": \"Add Variable\",\n \"loading\": \"Loading…\",\n \"retry\": \"Retry\",\n \"of\": \"of\",\n \"selected\": \"selected\",\n \"variable\": \"variable\",\n \"variables\": \"variables\",\n \"showValue\": \"Show value\",\n \"hideValue\": \"Hide value\"\n },\n \"modelSelector\": {\n \"selectModel\": \"Select model\",\n \"noConfiguredModels\": \"No configured models\",\n \"noFreeModels\": \"No free models available\",\n \"switchFailed\": \"Failed to switch model\",\n \"searchModels\": \"Search models...\",\n \"noModelsFound\": \"No matching models found\",\n \"free\": \"Free\",\n \"vision\": \"Vision\",\n \"freeBannerText\": \"Free models may have rate limits and limited availability. Refer to the provider for details.\",\n \"moreProviders\": \"More free providers (API Key required)\",\n \"proBannerText\": \"Showing models configured in Settings.\",\n \"connectToUse\": \"Connect {{provider}} to use free models\",\n \"configureApiKey\": \"Configure {{provider}} API Key to use free models\",\n \"configureApiKeyTitle\": \"Go to Configuration\",\n \"configureApiKeyConfirm\": \"You will be redirected to the Models page to configure {{provider}} API Key. Continue?\",\n \"goToConfigure\": \"Go to Configure\",\n \"noModelsDiscovered\": \"Connected. Models loading...\",\n \"oauthTitle\": \"Connect to {{provider}}\",\n \"oauthDescription\": \"A new browser window will open for you to authorize access to {{provider}}. Your API key will be saved automatically.\",\n \"oauthContinue\": \"Continue\",\n \"oauthWaiting\": \"Waiting for authorization...\",\n \"oauthWaitingDescription\": \"Please complete the authorization in the browser window.\",\n \"oauthConnected\": \"Successfully connected to {{provider}}\",\n \"oauthFailed\": \"Authorization failed, please try again\",\n \"viewMore\": \"View more ({{count}})\"\n },\n \"tokenUsage\": {\n \"title\": \"Token Usage\",\n \"totalTokens\": \"Total Tokens\",\n \"totalCalls\": \"Total Calls\",\n \"promptTokens\": \"Prompt Tokens\",\n \"completionTokens\": \"Completion Tokens\",\n \"byModel\": \"By Model\",\n \"byDate\": \"By Date\",\n \"model\": \"Model\",\n \"date\": \"Date\",\n \"loadFailed\": \"Failed to load token usage\",\n \"noData\": \"No token usage data in the selected period\",\n \"modelTrend\": \"Model Usage Trend\",\n \"tokenTypeChart\": \"Token Type Trend\",\n \"selectAll\": \"Select All\",\n \"allSelected\": \"All\",\n \"itemsSelected\": \"{{count}} selected\",\n \"selectModels\": \"Select models\",\n \"selectTokenTypes\": \"Select token types\"\n },\n \"agentStats\": {\n \"title\": \"Agent Statistics\",\n \"description\": \"View agent statistics for the selected time period.\",\n \"totalSessions\": \"Total Sessions\",\n \"totalMessages\": \"Total Messages\",\n \"userMessages\": \"User Messages\",\n \"assistantMessages\": \"Assistant Messages\",\n \"promptTokens\": \"Prompt Tokens\",\n \"completionTokens\": \"Completion Tokens\",\n \"llmCalls\": \"LLM Calls\",\n \"toolCalls\": \"Tool Calls\",\n \"channel\": \"Channel\",\n \"sessionCount\": \"Sessions\",\n \"sessions\": \"Sessions\",\n \"newSessions\": \"New Sessions\",\n \"activeSessions\": \"Active Sessions\",\n \"messages\": \"Messages\",\n \"messageTrend\": \"Message Trend\",\n \"sessionTrend\": \"Session Trend\",\n \"tokenTrend\": \"Token Trend\",\n \"llmAndToolTrend\": \"LLM & Tool Call Trend\",\n \"sessionsByChannel\": \"Sessions by Channel\",\n \"messagesByChannel\": \"Messages by Channel\",\n \"totalSessionsTooltip\": \"Total chat sessions created, each session is an independent conversation context\",\n \"totalMessagesTooltip\": \"Total messages across all sessions, including both user and assistant messages\",\n \"promptTokensTooltip\": \"Total prompt tokens sent to the LLM\",\n \"completionTokensTooltip\": \"Total completion tokens generated by the LLM\",\n \"llmCallsTooltip\": \"Number of LLM API calls, each request counts as one call\",\n \"toolCallsTooltip\": \"Number of times the agent decided to invoke a tool; a single LLM call may produce multiple tool-use intents\",\n \"messageTrendTooltip\": \"Trend comparing user messages vs assistant messages\",\n \"sessionTrendTooltip\": \"Trend comparing new sessions vs active sessions; orange bars are sessions created that day, blue bars are sessions with messages that day\",\n \"tokenTrendTooltip\": \"Trend of prompt and completion token consumption\",\n \"llmAndToolTrendTooltip\": \"Trend comparing LLM calls vs tool-use intents\",\n \"sessionsByChannelTooltip\": \"Distribution of sessions by messaging channel; sessions without a channel are grouped under Console\",\n \"messagesByChannelTooltip\": \"Distribution of total messages by messaging channel\",\n \"loadFailed\": \"Failed to load statistics\",\n \"noData\": \"No statistics data in the selected period\",\n \"retry\": \"Retry\"\n },\n \"models\": {\n \"llmConfiguration\": \"LLM Configuration\",\n \"providersTitle\": \"Providers\",\n \"providersDescription\": \"Configure API keys and endpoints for each provider.\",\n \"llmTitle\": \"Models\",\n \"defaultLlm\": \"Default LLM\",\n \"notConfiguredYet\": \"Not configured\",\n \"llmDescription\": \"Set the global default LLM model from an authorized provider. You can choose a specific model for each Agent in the Chat page.\",\n \"configureProvider\": \"Configure {{name}}\",\n \"baseURL\": \"Base URL\",\n \"apiKey\": \"API Key\",\n \"advancedConfig\": \"Advanced Configuration\",\n \"authMode\": \"Authentication Mode\",\n \"authModeApiKey\": \"API Key (x-api-key)\",\n \"authModeAuthToken\": \"Auth Token (Authorization: Bearer)\",\n \"customHeaders\": \"Custom Headers\",\n \"customHeadersHint\": \"Appended to every API request, e.g. x-app: cli\",\n \"customHeaderKey\": \"Header Name\",\n \"customHeaderValue\": \"Header Value\",\n \"addHeader\": \"+ Add Header\",\n \"generateConfig\": \"Generation Parameters\",\n \"generateConfigHint\": \"Generation parameters expressed as a JSON object. They will be expanded and passed into the generation request (`openai.chat.completions` or `anthropic.messages`).\",\n \"generateConfigInvalidJson\": \"Please enter valid JSON\",\n \"generateConfigMustBeObject\": \"Generation parameters must be a JSON object\",\n \"modelAdvancedConfig\": \"Model Advanced Config\",\n \"modelGenerateConfig\": \"Model Generation Parameters\",\n \"modelGenerateConfigHint\": \"Generation parameters expressed as a JSON object for this model. They will be expanded and passed into the generation request. These override provider-level advanced configuration.\",\n \"modelConfigSaved\": \"Configuration saved for model {{name}}\",\n \"modelConfigSaveFailed\": \"Failed to save model configuration\",\n \"modelConfigLabel\": \"Model Config\",\n \"maxTokensLabel\": \"Max Tokens\",\n \"maxTokensHint\": \"Maximum output tokens per response\",\n \"maxInputLengthLabel\": \"Max Context Length\",\n \"maxInputLengthHint\": \"Model context window size, controls context compaction threshold (≥1000)\",\n \"relayReasoningLabel\": \"Relay Reasoning\",\n \"relayReasoningHint\": \"Relay reasoning contents back to subsequent conversation turns\",\n \"thinkingModeLabel\": \"Enable Thinking\",\n \"thinkingModeHint\": \"Turn on to enable model thinking/reasoning. Turn off to disable thinking.\",\n \"thinkingBudgetLabel\": \"Thinking Budget\",\n \"thinkingBudgetHint\": \"Using model default thinking budget\",\n \"switchToManual\": \"Manual\",\n \"switchToAuto\": \"Auto\",\n \"reasoningEffortLabel\": \"Reasoning Effort\",\n \"reasoningEffortHint\": \"Controls reasoning depth (OpenAI-family)\",\n \"protocol\": \"Protocol\",\n \"protocolHint\": \"Choose the provider API protocol for this configuration.\",\n \"selectProtocol\": \"Please select a protocol\",\n \"protocolOpenAI\": \"OpenAI-compatible (Chat Completions)\",\n \"protocolOpenAIResponse\": \"OpenAI-compatible (Response API)\",\n \"protocolAnthropic\": \"Anthropic (Messages API)\",\n \"currentKey\": \"Current: {{key}}\",\n \"startsWith\": \"Starts with \\\"{{prefix}}\\\"\",\n \"optionalSelfHosted\": \"Optional for self-hosted services\",\n \"leaveBlankKeep\": \"Leave blank to keep current key\",\n \"enterApiKey\": \"Enter API key ({{prefix}}...)\",\n \"enterApiKeyOptional\": \"Enter API key (optional)\",\n \"openAIEndpoint\": \"OpenAI endpoint, e.g. https://api.openai.com/v1\",\n \"openAICompatibleEndpoint\": \"OpenAI-compatible endpoint, e.g. https://api.example.com (append /v1 only if your provider requires it)\",\n \"azureEndpointHint\": \"Azure OpenAI endpoint, e.g. https://<resource>.openai.azure.com/openai/v1\",\n \"anthropicEndpointHint\": \"Anthropic endpoint, e.g. https://api.anthropic.com\",\n \"ollamaEndpointHint\": \"Ollama endpoint, e.g. http://localhost:11434\",\n \"lmstudioEndpointHint\": \"LM Studio endpoint, e.g. http://localhost:1234/v1\",\n \"apiEndpointHint\": \"API endpoint, e.g. https://api.example.com\",\n \"pleaseEnterBaseURL\": \"Please enter the API base URL\",\n \"selectBaseURL\": \"Select a base URL\",\n \"selectBaseURLHint\": \"Select the regional endpoint for this provider\",\n \"pleaseEnterValidURL\": \"Please enter a valid URL\",\n \"apiKeyShouldStart\": \"API Key should start with \\\"{{prefix}}\\\"\",\n \"configurationSaved\": \"{{name}} configuration saved\",\n \"failedToSaveConfig\": \"Failed to save configuration\",\n \"revokeAuthorization\": \"Revoke Authorization\",\n \"revokeConfirmContent\": \"Are you sure you want to remove the API key for {{name}}? The current LLM model configuration will also be cleared.\",\n \"revokeConfirmSimple\": \"Are you sure you want to remove the API key for {{name}}?\",\n \"authorizationRevoked\": \"{{name}} authorization revoked, LLM model cleared\",\n \"authorizationRevokedSimple\": \"{{name}} authorization revoked\",\n \"failedToRevoke\": \"Failed to revoke authorization\",\n \"active\": \"Active: {{provider}} / {{model}}\",\n \"provider\": \"Provider\",\n \"model\": \"Model\",\n \"selectProvider\": \"Select provider (must be authorized)\",\n \"selectModel\": \"Select a model\",\n \"llmModelUpdated\": \"LLM Model updated\",\n \"failedToSave\": \"Failed to save\",\n \"freeModelWarningTitle\": \"Free Model Notice\",\n \"freeModelWarningMessage\": \"Free models may have limited availability. Please refer to the provider's terms of service:\",\n \"freeModelWarningDontShowAgain\": \"Do not show this again\",\n \"saved\": \"Saved\",\n \"save\": \"Save\",\n \"cancel\": \"Cancel\",\n \"loading\": \"Loading...\",\n \"retry\": \"Retry\",\n \"notSet\": \"Not set\",\n \"notRequired\": \"Not required\",\n \"available\": \"Available\",\n \"unavailable\": \"Unavailable\",\n \"providerAvailable\": \"Ready (with models)\",\n \"providerNoModels\": \"Not Ready (no models)\",\n \"providerNotConfigured\": \"Not Ready (not configured)\",\n \"settings\": \"Settings\",\n \"actions\": \"Actions\",\n \"search\": \"Search\",\n \"searchPlaceholder\": \"Search providers...\",\n \"searchModelPlaceholder\": \"Search models...\",\n \"custom\": \"Custom\",\n \"builtin\": \"Built-in\",\n \"freeTierGroup\": \"Free Tier\",\n \"paidGroup\": \"Paid\",\n \"localCustomGroup\": \"Local & Custom\",\n \"cloudGroup\": \"Cloud Providers\",\n \"configuredGroup\": \"Configured\",\n \"configuredOnline\": \"providers online\",\n \"noConfigured\": \"No providers configured yet. Select one below to get started.\",\n \"goConfigureBtn\": \"Configure a Provider\",\n \"availableGroup\": \"Available Providers\",\n \"clickToConfigure\": \"Click to configure\",\n \"configureAction\": \"Configure →\",\n \"endpointCount\": \"{{count}} endpoints\",\n \"connect\": \"OAuth\",\n \"saveApiKey\": \"Save\",\n \"addProvider\": \"Add Provider\",\n \"addProviderTitle\": \"Add Custom Provider\",\n \"providerIdLabel\": \"Provider ID\",\n \"providerIdPlaceholder\": \"e.g. openai, google, anthropic\",\n \"providerIdHint\": \"Lowercase letters, digits, hyphens, underscores. Cannot be changed later.\",\n \"providerNameLabel\": \"Display Name\",\n \"providerNamePlaceholder\": \"e.g. OpenAI, Google Gemini\",\n \"defaultBaseUrlLabel\": \"Default Base URL\",\n \"defaultBaseUrlPlaceholder\": \"e.g. https://api.example.com\",\n \"apiKeyPrefixLabel\": \"API Key Prefix (optional)\",\n \"apiKeyPrefixPlaceholder\": \"e.g. sk-\",\n \"providerCreated\": \"Provider \\\"{{name}}\\\" created\",\n \"providerCreateFailed\": \"Failed to create provider\",\n \"deleteProvider\": \"Delete Provider\",\n \"deleteProviderConfirm\": \"Delete custom provider \\\"{{name}}\\\" and all its models? This cannot be undone.\",\n \"providerDeleted\": \"Provider \\\"{{name}}\\\" deleted\",\n \"providerDeleteFailed\": \"Failed to delete provider\",\n \"disableProvider\": \"Disable Provider\",\n \"disableProviderConfirm\": \"Clear API key for \\\"{{name}}\\\"? The provider will become unconfigured.\",\n \"disableBtn\": \"Disable\",\n \"changeApiKey\": \"Change\",\n \"selectVariant\": \"Select {{name}} Variant\",\n \"providerDisabled\": \"Provider \\\"{{name}}\\\" disabled\",\n \"manageModels\": \"Models\",\n \"models\": \"Models\",\n \"manageModelsTitle\": \"{{provider}} — Model Management\",\n \"userAdded\": \"User-added\",\n \"addModel\": \"Add Model\",\n \"addModels\": \"Add Models\",\n \"autoDiscoverModels\": \"Auto Discover Models\",\n \"addModelTitle\": \"Add Model to {{provider}}\",\n \"modelIdLabel\": \"Model ID\",\n \"modelIdPlaceholder\": \"e.g. gpt-4o, gemini-2.0-flash\",\n \"modelDiscoveryUnavailableHint\": \"No available models were found. This provider may not support model list discovery, so please enter the model ID manually.\",\n \"modelNameLabel\": \"Model Name\",\n \"modelNameRequired\": \"Please enter model name\",\n \"modelNamePlaceholder\": \"e.g. GPT-4o, Gemini 2.0 Flash\",\n \"modelAdded\": \"Model \\\"{{name}}\\\" added\",\n \"modelAddFailed\": \"Failed to add model\",\n \"modelAlreadyExists\": \"Model ID \\\"{{id}}\\\" already exists\",\n \"autoDiscoverModelsSuccess\": \"Auto discovery complete. Added {{count}} models\",\n \"autoDiscoverModelsNoNew\": \"Auto discovery complete. No new models were added\",\n \"autoDiscoverModelsFailed\": \"Failed to auto discover models\",\n \"removeModel\": \"Remove\",\n \"removeModelConfirm\": \"Remove model \\\"{{name}}\\\" from {{provider}}?\",\n \"modelRemoved\": \"Model \\\"{{name}}\\\" removed\",\n \"modelRemoveFailed\": \"Failed to remove model\",\n \"loadMore\": \"Load more ({{count}})\",\n \"modelsCount\": \"{{count}} models\",\n \"noModels\": \"No models\",\n \"addModelFirst\": \"Please add a model first\",\n \"modelList\": \"Model List\",\n \"clearAll\": \"Clear All\",\n \"filteredModelsLoaded\": \"Filtered models loaded: {{count}}\",\n \"filterFailed\": \"Failed to filter models\",\n \"clearAllModels\": \"Clear All Models\",\n \"clearAllModelsConfirm\": \"Clear all {{count}} extra models? This cannot be undone.\",\n \"allModelsCleared\": \"Cleared {{count}} models\",\n \"filterModels\": \"Filter Models\",\n \"filterByProvider\": \"Provider:\",\n \"searchProviderPlaceholder\": \"Search provider name\",\n \"selectAllProviders\": \"Select All\",\n \"clearProviderSelection\": \"Deselect All\",\n \"noMatchingProviders\": \"No matching providers\",\n \"filterByModality\": \"Input Modality:\",\n \"filterFreeOnly\": \"Free Models Only:\",\n \"getModels\": \"Get Models\",\n \"discovered\": \"Available Models:\",\n \"add\": \"Add\",\n \"local\": \"Local\",\n \"localType\": \"Type\",\n \"localEmbedded\": \"Embedded (in-process)\",\n \"localDownloadFirst\": \"Model service not started\",\n \"localDownloadModel\": \"Download Model\",\n \"localModelsTitle\": \"{{provider}} — Local Models\",\n \"localNoModels\": \"No downloaded models yet\",\n \"localRepoId\": \"Model Repository ID\",\n \"localRepoIdRequired\": \"Please enter a model repository ID\",\n \"localRepoIdPlaceholder\": \"e.g. Qwen/Qwen3-0.6B-GGUF\",\n \"localFilename\": \"Filename (optional)\",\n \"localFilenamePlaceholder\": \"e.g. mistral-7b.Q4_K_M.gguf\",\n \"localFilenameHint\": \"Leave empty to auto-select the best quantization\",\n \"localSource\": \"Source\",\n \"localDownloadSuccess\": \"Model downloaded successfully\",\n \"localDownloadFailed\": \"Failed to download model\",\n \"localDeleteModel\": \"Delete Model\",\n \"localDeleteConfirm\": \"Delete local model \\\"{{name}}\\\"? The model file will be removed from disk.\",\n \"localModelDeleted\": \"Model \\\"{{name}}\\\" deleted\",\n \"localDeleteFailed\": \"Failed to delete model\",\n \"localDownloadPending\": \"Preparing to download...\",\n \"localDownloading\": \"Downloading {{repo}}... This may take a few minutes.\",\n \"localDownloadNavigateHint\": \"You can navigate away — the download will continue in the background.\",\n \"localDownloadInProgress\": \"A download is already in progress\",\n \"localDownloaded\": \"Downloaded\",\n \"localNotDownloaded\": \"Not downloaded\",\n \"localNoRecommendedModels\": \"This device is not powerful enough to run local models\",\n \"localCancelDownloadTitle\": \"Cancel download\",\n \"localCancelDownloadAction\": \"Cancel download\",\n \"localCancelDownloadConfirm\": \"Cancel download of \\\"{{repo}}\\\"?\",\n \"localDownloadCancelled\": \"Download cancelled\",\n \"localCancelDownloadFailed\": \"Failed to cancel download\",\n \"localLlamacppName\": \"Inference engine\",\n \"localLlamacppInstallHint\": \"Install the local runtime before downloading or starting a model.\",\n \"localInstallLlamacpp\": \"Install llama.cpp\",\n \"localLlamacppInstallStarted\": \"llama.cpp download started\",\n \"localLlamacppInstallSuccess\": \"llama.cpp installed successfully\",\n \"localLlamacppInstallFailed\": \"Failed to install llama.cpp\",\n \"localRuntimeSectionTitle\": \"Inference Engine\",\n \"localRuntimeSectionDescription\": \"Powered by Llama.cpp\",\n \"localRuntimeComputeHint\": \"CPU is used by default. For GPU acceleration, use Ollama or LM Studio.\",\n \"localRuntimeInstalled\": \"Installed\",\n \"localRuntimeUpdateAvailable\": \"Update available\",\n \"localRuntimeUpdateAction\": \"Click to download the latest version\",\n \"localRuntimeUpdateConfirmTitle\": \"Confirm llama.cpp update\",\n \"localRuntimeUpdateConfirmContent\": \"Updating will overwrite the currently installed llama.cpp version. Confirm to start downloading and installing the latest version.\",\n \"localRuntimeUpdateConfirmContentWithServer\": \"Updating will overwrite the currently installed llama.cpp version and stop the currently running model service ({{model}}). Confirm to start downloading and installing the latest version.\",\n \"localRuntimeUnsupported\": \"Unsupported\",\n \"localRuntimeMissing\": \"Missing\",\n \"localServerRunningTitle\": \"Current local server\",\n \"localServerOnline\": \"Running\",\n \"localServerIdle\": \"Dead\",\n \"localServerUnavailable\": \"Issue\",\n \"localNoActiveModel\": \"No active model\",\n \"localNoDownloadedModelsHint\": \"Download a suitable model before starting the local inference service.\",\n \"localDownloadModelFirst\": \"Download the model first\",\n \"localEngineInstallStateLabel\": \"Install\",\n \"localEngineRunStateLabel\": \"Status\",\n \"localEngineCurrentModelLabel\": \"Current served model\",\n \"localEngineNextStepLabel\": \"Next step\",\n \"localEngineHealthLabel\": \"Engine health\",\n \"localEngineStatusHint\": \"Please download and start a suitable model from the list below\",\n \"localStartServer\": \"Start\",\n \"localStopServer\": \"Stop\",\n \"localServerStartFailed\": \"Failed to start local server\",\n \"localServerStopFailed\": \"Failed to stop local server\",\n \"localServerSwitchTitle\": \"Switch running model\",\n \"localServerSwitchConfirm\": \"The current server is running {{current}}. Switch to {{next}}?\",\n \"localSwitchModel\": \"Switch\",\n \"localModelPathMissing\": \"This downloaded model is missing a local path.\",\n \"localModelsSectionTitle\": \"Local Models\",\n \"localModelsSectionDescription\": \"Downloaded models can be started immediately. Only one model download and one local server instance can run at a time.\",\n \"localModelsLockedHint\": \"Please install llama.cpp first\",\n \"localAlternativeRuntimeHint\": \"Please try Ollama or LM Studio\",\n \"localModelsCount\": \"Downloaded {{downloaded}} / {{total}}\",\n \"localCurrentDownloadTitle\": \"Current download\",\n \"localAdvancedConfigTitle\": \"Advanced Local Settings\",\n \"localAdvancedConfigDescription\": \"These settings apply to the embedded local inference runtime. Max context length changes take effect the next time the local server starts.\",\n \"localAdvancedConfigSaved\": \"Advanced local settings saved\",\n \"localAdvancedConfigSaveFailed\": \"Failed to save advanced local settings\",\n \"localMaxContextLengthLabel\": \"Max Context Length\",\n \"localMaxContextLengthHint\": \"Maximum token count for the model context window, determining how much history fits in each request. Changes take effect the next time the local server starts.\",\n \"localMaxContextLengthRequired\": \"Please enter max context length\",\n \"localMaxContextLengthMin\": \"Max context length must be at least {{min}}\",\n \"localMaxContextLengthPlaceholder\": \"Enter max context length\",\n \"localServerPortLabel\": \"Server Port\",\n \"localServerPortHint\": \"Leave this empty to let llama.cpp bind an available port automatically. Enter a port to reuse that fixed port on future startups. Changes take effect the next time the local server starts.\",\n \"localServerPortRange\": \"Server port must be between {{min}} and {{max}}\",\n \"localServerPortPlaceholder\": \"Leave empty to auto-select a port\",\n \"localGenerateConfigHint\": \"Configure local model generation parameters as a JSON object. These fields will be expanded into subsequent generation requests.\",\n \"localCustomModelTitle\": \"Custom model download\",\n \"localCustomModelHint\": \"Enter a model repository ID. GGUF format only.\",\n \"localSourceHuggingFace\": \"Hugging Face\",\n \"localSourceModelScope\": \"ModelScope\",\n \"ollamaModelNamePlaceholder\": \"e.g. mistral:7b, qwen3:8b\",\n \"testConnection\": \"Test Connection\",\n \"testConnectionSuccess\": \"Connection test successful\",\n \"testConnectionFailed\": \"Connection test failed\",\n \"testConnectionFailedWithMessage\": \"Connection test failed: {{message}}\",\n \"testConnectionError\": \"An error occurred while testing connection\",\n \"discoverModels\": \"Discover Models\",\n \"discoverModelsFailed\": \"Failed to discover models\",\n \"ollamaFetchModelsFailed\": \"Failed to fetch Ollama models. Please check the error details.\",\n \"modelTestFailed\": \"Model validation failed, please check if the model ID is correct\",\n \"modelTestFailedConfirm\": \"Model connection test failed: {{message}}. Do you still want to add this model?\",\n \"autoDiscoveredAndAdded\": \"Auto-discovered {{count}} models and added {{added}} new model(s)\",\n \"autoDiscoveredNoNew\": \"Auto-discovered {{count}} models; model list is already up to date\",\n \"autoDiscoverFailed\": \"Automatic model discovery failed; you can add models manually\",\n \"tagImage\": \"Image\",\n \"tagVideo\": \"Video\",\n \"tagTextOnly\": \"Text Only\",\n \"tagMultimodal\": \"Multimodal\",\n \"tagVision\": \"Vision\",\n \"tagText\": \"Text\",\n \"free\": \"Free\",\n \"probeMultimodal\": \"Test Multimodal\",\n \"probeImage\": \"Image\",\n \"probeVideo\": \"Video\",\n \"probeSupported\": \"Supported: {{types}}\",\n \"probeNotSupported\": \"This model does not support multimodal input\",\n \"probeFailed\": \"Probe failed\",\n \"tagNotProbed\": \"Not Probed\",\n \"modalityVision\": \"Image\",\n \"modalityAudio\": \"Audio\",\n \"modalityVideo\": \"Video\",\n \"modalityFile\": \"File\",\n \"modalityText\": \"Text only\",\n \"perMillionIn\": \"/1M in\",\n \"perMillionOut\": \"/1M out\"\n },\n \"agentConfig\": {\n \"title\": \"Configuration\",\n \"description\": \"Configure agent runtime parameters\",\n \"reactAgentTitle\": \"ReAct Agent\",\n \"llmRetryTitle\": \"LLM Auto Retry\",\n \"llmRetryEnabled\": \"Enable Auto Retry\",\n \"llmRetryEnabledTooltip\": \"Automatically retry transient LLM API failures such as rate limits, timeouts, or connection errors. Applies to new requests after saving.\",\n \"llmMaxRetries\": \"Max Retries\",\n \"llmMaxRetriesTooltip\": \"Maximum number of extra retry attempts for a retryable LLM request failure.\",\n \"llmMaxRetriesPlaceholder\": \"Enter max retries\",\n \"llmMaxRetriesRequired\": \"Max retries is required\",\n \"llmMaxRetriesMin\": \"Max retries must be at least 1\",\n \"llmBackoffBase\": \"Backoff Base Delay (s)\",\n \"llmBackoffBaseTooltip\": \"Base delay before the first retry. Later retries use exponential backoff from this value.\",\n \"llmBackoffBasePlaceholder\": \"Enter base delay\",\n \"llmBackoffBaseRequired\": \"Backoff base delay is required\",\n \"llmBackoffBaseMin\": \"Backoff base delay must be at least 0.1 seconds\",\n \"llmBackoffCap\": \"Backoff Max Delay (s)\",\n \"llmBackoffCapTooltip\": \"Maximum cap for exponential retry backoff delays.\",\n \"llmBackoffCapPlaceholder\": \"Enter max delay\",\n \"llmBackoffCapRequired\": \"Backoff max delay is required\",\n \"llmBackoffCapMin\": \"Backoff max delay must be at least 0.5 seconds\",\n \"llmBackoffCapGteBase\": \"Backoff max delay must be greater than or equal to the base delay\",\n \"llmRateLimiterTitle\": \"LLM Rate Limiter\",\n \"llmMaxConcurrent\": \"Max Concurrent Calls\",\n \"llmMaxConcurrentTooltip\": \"Maximum number of LLM calls allowed in-flight at the same time. Shared across all agents; only the first initialization takes effect until restart.\",\n \"llmMaxConcurrentPlaceholder\": \"Enter max concurrent calls\",\n \"llmMaxConcurrentRequired\": \"Max concurrent calls is required\",\n \"llmMaxConcurrentRange\": \"Max concurrent calls must be at least 1\",\n \"llmMaxQpm\": \"Max QPM (Queries Per Minute)\",\n \"llmMaxQpmTooltip\": \"Maximum requests allowed within a 60-second sliding window. Requests exceeding this limit wait before being dispatched — proactively preventing 429 rate-limit errors. 0 = disabled.\",\n \"llmMaxQpmPlaceholder\": \"Enter max QPM (0 = unlimited)\",\n \"llmMaxQpmRequired\": \"Max QPM is required\",\n \"llmMaxQpmRange\": \"Max QPM must be at least 0\",\n \"llmRateLimitPause\": \"Rate Limit Pause (s)\",\n \"llmRateLimitPauseTooltip\": \"Default pause duration (seconds) applied globally when a 429 rate-limit response is received. Overridden by the API's Retry-After header when present.\",\n \"llmRateLimitPausePlaceholder\": \"Enter pause duration\",\n \"llmRateLimitPauseRequired\": \"Rate limit pause is required\",\n \"llmRateLimitPauseMin\": \"Rate limit pause must be at least 1 second\",\n \"llmRateLimitJitter\": \"Rate Limit Jitter (s)\",\n \"llmRateLimitJitterTooltip\": \"Random jitter range (seconds) added on top of the pause so concurrent waiters stagger their wake-up and avoid a new burst.\",\n \"llmRateLimitJitterPlaceholder\": \"Enter jitter range\",\n \"llmRateLimitJitterRequired\": \"Rate limit jitter is required\",\n \"llmRateLimitJitterMin\": \"Rate limit jitter must be at least 0 seconds\",\n \"llmAcquireTimeout\": \"Slot Acquire Timeout (s)\",\n \"llmAcquireTimeoutTooltip\": \"Maximum time (seconds) a caller will wait to acquire a rate-limiter slot before giving up with an error.\",\n \"llmAcquireTimeoutPlaceholder\": \"Enter acquire timeout\",\n \"llmAcquireTimeoutRequired\": \"Slot acquire timeout is required\",\n \"llmAcquireTimeoutMin\": \"Slot acquire timeout must be at least 10 seconds\",\n \"llmAcquireTimeoutGtPauseJitter\": \"Slot acquire timeout must be greater than rate limit pause + jitter\",\n \"contextManagementTitle\": \"Context Management\",\n \"memoryManagerBackend\": \"Long-term Memory Management Backend\",\n \"memoryManagerBackendTooltip\": \"Backend for the memory manager. Choose remelight (local files), adbpg (cloud database), or none (disable memory).\",\n \"memoryManagerBackendRestartWarning\": \"Switching the long-term memory management backend does not support hot reload. Save and restart QwenPaw for changes to take effect.\",\n \"maxIters\": \"Max Iterations\",\n \"maxItersTooltip\": \"Maximum number of reasoning-acting iterations for ReAct agent\",\n \"maxItersPlaceholder\": \"Enter max iterations\",\n \"maxItersRequired\": \"Max iterations is required\",\n \"maxItersMin\": \"Max iterations must be at least 1\",\n \"agentLoopTitle\": \"Agent Loop Settings\",\n \"loopMode\": {\n \"builtIn\": \"Built-in\",\n \"builtInNote\": \"Pipeline locked · Values editable\",\n \"templateSectionTitle\": \"Loop templates\",\n \"templateSectionDescription\": \"Choose a built-in template or add your own.\",\n \"defaultTab\": \"Default\",\n \"goalTab\": \"Goal\",\n \"missionTab\": \"Mission\",\n \"defaultDescription\": \"The standard guarded ReAct loop used outside an explicit mode.\",\n \"goalDescription\": \"A bounded persistent loop for concrete, verifiable goals.\",\n \"missionDescription\": \"A persistent pipeline for longer-running, multi-step missions.\",\n \"gatePipeline\": \"Gate pipeline\",\n \"repetitionTitle\": \"Repetition protection\",\n \"repetitionDescription\": \"Detect repeated tool calls and intervene.\",\n \"iterationTitle\": \"Iteration limit\",\n \"iterationDescription\": \"Bound the number of ReAct iterations.\",\n \"retryTitle\": \"Qualitative completion check\",\n \"retryDescription\": \"Triggered when the agent produces a text response without tool calls.\",\n \"goalTurnTitle\": \"Goal turn limit\",\n \"goalTurnDescription\": \"Limit turns within one active goal.\",\n \"maxGoalTurns\": \"Maximum goal turns\",\n \"goalBudgetTitle\": \"Goal token budget\",\n \"goalBudgetDescription\": \"Stop when the complete goal reaches its budget.\",\n \"maxGoalTokens\": \"Maximum goal tokens\",\n \"goalCompletionTitle\": \"Goal completion check\",\n \"goalCompletionDescription\": \"Read the explicit goal status before stopping.\",\n \"goalCompletionReadOnly\": \"Completion is controlled by the built-in goal status protocol.\",\n \"missionProgressTitle\": \"Mission progress check\",\n \"missionProgressDescription\": \"Continue until mission stories pass or the limit is reached.\",\n \"missionProgressReadOnly\": \"Completion is controlled by the built-in Mission story status.\",\n \"missionIterationTitle\": \"Mission iteration limit\",\n \"missionIterationDescription\": \"Set the default execution limit for new Missions.\",\n \"workerAttemptsTitle\": \"Worker attempts\",\n \"workerAttemptsDescription\": \"Limit Worker retries after a Story fails verification.\",\n \"missionVerificationTitle\": \"Mission verification policy\",\n \"missionVerificationDescription\": \"Define the default verification guidance and test command.\",\n \"defaultMaxMissionIterations\": \"Default maximum execution rounds\",\n \"defaultMaxMissionIterationsTooltip\": \"Used when /mission does not specify --max-iterations.\",\n \"maxRetriesPerStory\": \"Maximum retries per Story\",\n \"maxRetriesPerStoryTooltip\": \"Worker retries after a failed or partial Verifier result.\",\n \"defaultVerificationInstructions\": \"Verification guidance (optional)\",\n \"defaultVerificationInstructionsHelp\": \"Natural-language guidance applied to every Mission verifier.\",\n \"defaultVerificationInstructionsPlaceholder\": \"For example: Check Windows path handling and inspect the rendered UI.\",\n \"defaultVerifyCommand\": \"Default test command (optional)\",\n \"defaultVerifyCommandHelp\": \"The verifier attempts this through normal tools and permissions. /mission --verify overrides it.\",\n \"defaultVerifyCommandPlaceholder\": \"For example: pytest -q\",\n \"enableRepetitionProtection\": \"Enable repetition protection\",\n \"removeRule\": \"Remove rule\",\n \"custom\": \"Custom\",\n \"customDescription\": \"Build a saved pipeline from QwenPaw-owned gates.\",\n \"duplicate\": \"Duplicate\",\n \"delete\": \"Delete\",\n \"displayName\": \"Display name\",\n \"duplicateModeName\": \"A loop mode with this name already exists.\",\n \"slashCommand\": \"Slash command\",\n \"description\": \"Description\",\n \"availableToAgent\": \"Available to this agent\",\n \"runsTopToBottom\": \"Runs from top to bottom\",\n \"addGate\": \"Add gate\",\n \"emptyPipeline\": \"Add at least one Gate to make this mode available.\",\n \"iterationGateTitle\": \"Iteration limit\",\n \"iterationGateDescription\": \"Stop after a fixed number of loop iterations.\",\n \"doomGateTitle\": \"Repetition protection\",\n \"doomGateDescription\": \"Detect repeated tool calls and change strategy.\",\n \"tokenGateTitle\": \"Token budget\",\n \"tokenGateDescription\": \"Limit prompt and completion token usage.\",\n \"timeoutGateTitle\": \"Loop time limit\",\n \"timeoutGateDescription\": \"Stop at the next Loop boundary after elapsed time; running work is not interrupted.\",\n \"toolBudgetGateTitle\": \"Tool-call budget\",\n \"toolBudgetGateDescription\": \"Limit all calls and selected tools.\",\n \"retryGateTitle\": \"Early-stop retry\",\n \"retryGateDescription\": \"Prompt the agent to verify before ending.\",\n \"rubricGateTitle\": \"Completion rubric\",\n \"rubricGateDescription\": \"Evaluate explicit criteria and revise when needed.\",\n \"qualitativeRubricTitle\": \"Qualitative completion check\",\n \"qualitativeRubricDescription\": \"Check text responses without tool calls using natural-language criteria.\",\n \"qualitativeRubric\": \"Natural-language completion criteria\",\n \"qualitativeRubricHelp\": \"Triggered only for text responses without tool calls. The agent continues when work remains.\",\n \"completionRubricTitle\": \"Completion signal check\",\n \"completionRubricDescription\": \"Check text responses without tool calls for a completion signal.\",\n \"completionRubricHelp\": \"Triggered after text responses without tool calls. The agent outputs the completion signal when done; otherwise it keeps working until the maximum evaluation rounds is reached.\",\n \"completionPrompt\": \"Completion check prompt\",\n \"completionPromptHelp\": \"Describe when the result is complete. QwenPaw automatically injects the completion-signal output instruction.\",\n \"completionSignal\": \"Completion signal\",\n \"completionSignalHelp\": \"QwenPaw injects this signal into the check prompt and instructs the agent to output only it. Matching is exact after trimming whitespace and ignoring case.\",\n \"removeCriterion\": \"Remove criterion {{number}}\",\n \"totalTokensHelp\": \"Maximum combined input and output tokens used by this turn.\",\n \"promptTokensHelp\": \"Includes system instructions, conversation context, tool schemas, and tool results.\",\n \"completionTokensHelp\": \"Maximum tokens generated by the model during this turn.\",\n \"maxIterations\": \"Maximum iterations\",\n \"historyWindow\": \"History window\",\n \"similarityThreshold\": \"Similarity threshold\",\n \"totalTokens\": \"Maximum total tokens\",\n \"promptTokens\": \"Maximum input tokens\",\n \"completionTokens\": \"Maximum output tokens\",\n \"maxSeconds\": \"Maximum seconds\",\n \"maxSecondsHelp\": \"Elapsed time is checked after the current model response or tool call completes. Running work is not interrupted.\",\n \"allToolCalls\": \"All tool calls\",\n \"perToolLimits\": \"Per-tool limits\",\n \"toolName\": \"Tool name\",\n \"callLimitAria\": \"{{name}} call limit\",\n \"removeToolLimitAria\": \"Remove {{name}} limit\",\n \"addToolLimit\": \"Add per-tool limit\",\n \"maxRetries\": \"Maximum retries\",\n \"retryInstruction\": \"Retry instruction\",\n \"passThreshold\": \"Pass threshold\",\n \"maxEvaluations\": \"Maximum evaluation rounds\",\n \"graderError\": \"Grader error\",\n \"stopSafely\": \"Stop safely\",\n \"verifyOnceMore\": \"Verify once more\",\n \"completionCriteria\": \"Completion criteria\",\n \"criterionPlaceholder\": \"Describe observable completion\",\n \"required\": \"Required\",\n \"weight\": \"Weight\",\n \"addCriterion\": \"Add criterion\",\n \"moveGateAria\": \"Move {{title}}\",\n \"removeGate\": \"Remove gate\",\n \"newModeName\": \"New Loop Mode\",\n \"defaultCustomDescription\": \"A custom gate pipeline.\",\n \"copyName\": \"{{name}} Copy\",\n \"createModeAria\": \"Create custom loop mode\",\n \"createModeTitle\": \"Create custom loop mode\",\n \"startingTemplate\": \"Starting template\",\n \"startingPipeline\": \"Starting pipeline\",\n \"blankPipelineHint\": \"Start empty and add gates later\",\n \"safeTemplate\": \"Safe run\",\n \"researchTemplate\": \"Budgeted research\",\n \"qualityTemplate\": \"Quality first\",\n \"blankTemplate\": \"Blank pipeline\"\n },\n \"doomLoopEnabled\": \"Repetition Protection\",\n \"doomLoopEnabledTooltip\": \"Automatically intervene when the agent gets stuck repeating the same actions\",\n \"doomLoopAfter\": \"After\",\n \"doomLoopRepetitions\": \"identical actions\",\n \"doomLoopStopAction\": \"Pause & Ask for Help\",\n \"doomLoopWarnAction\": \"Send Reminder\",\n \"doomLoopSimpleMode\": \"Simple\",\n \"doomLoopAdvancedMode\": \"Advanced\",\n \"doomLoopWindowSize\": \"Detection Range\",\n \"doomLoopWindowSizeTooltip\": \"How many recent actions to check for repetition\",\n \"doomLoopSimilarity\": \"Match Sensitivity\",\n \"doomLoopSimilarityTooltip\": \"How similar actions must be to count as repetition (lower = stricter)\",\n \"doomLoopStages\": \"Intervention Rules\",\n \"doomLoopAction\": \"Action\",\n \"doomLoopPrompt\": \"Message\",\n \"doomLoopPromptPlaceholder\": \"Reminder message or pause reason...\",\n \"doomLoopAddStage\": \"Add Rule\",\n \"iterationTitle\": \"Iteration Limit\",\n \"iterationEnabled\": \"Enable Iteration Limit\",\n \"iterationEnabledTooltip\": \"Stop the agent after a fixed number of loop turns\",\n \"iterationMaxIterations\": \"Maximum Iterations\",\n \"iterationMaxIterationsTooltip\": \"Maximum number of loop turns before stopping\",\n \"rubricTitle\": \"Qualitative Completion Check\",\n \"rubricEnabled\": \"Enable Qualitative Completion Check\",\n \"rubricEnabledTooltip\": \"Triggered only when the agent produces a text response without tool calls.\",\n \"rubricPrompt\": \"Natural-Language Completion Criteria\",\n \"rubricPromptTooltip\": \"The prompt injected to ask the agent to evaluate task completion\",\n \"rubricDesc\": \"When the agent produces a text response without tool calls, apply natural-language completion criteria and ask it to continue when work remains.\",\n \"rubricPromptPlaceholder\": \"You did not call any tool. If the task is complete, confirm. Otherwise, continue with tool calls.\",\n \"rubricMaxInterventions\": \"Maximum Evaluation Rounds\",\n \"rubricMaxInterventionsTooltip\": \"Maximum completion checks for text responses without tool calls during one turn.\",\n \"inLoopModes\": \"Enable in /goal & /mission modes\",\n \"inLoopModesTooltip\": \"Also apply this gate when /goal or /mission loop mode is active\",\n \"comingSoonEditable\": \"Custom configuration will be available in a future release.\",\n \"simpleMode\": \"Simple\",\n \"advancedMode\": \"Advanced\",\n \"comingSoon\": \"Coming Soon\",\n \"reactModeTab\": \"Loop Template - Default\",\n \"goalModeTab\": \"Loop Template - Goal\",\n \"missionModeTab\": \"Loop Template - Mission\",\n \"goalModeInfoTitle\": \"Goal Mode vs Default\",\n \"goalModeInfo\": \"Default mode stops after producing a reply. Goal mode keeps the agent looping toward a goal, using Rubric evaluation to determine completion. All operations run within the current agent context.\",\n \"missionModeInfoTitle\": \"Mission Mode vs Goal Mode\",\n \"missionModeInfo\": \"Mission mode auto-decomposes complex tasks into sub-tasks, executed by independent Worker sub-agents and verified by Verifier sub-agents. Each sub-task runs in its own context, preventing context pollution of the main session. Best for complex multi-step engineering tasks.\",\n \"goalIterationGate\": \"Goal Iteration Gate\",\n \"goalIterationGateDesc\": \"Limits the number of agent turns within a goal session. Tracks iteration count and token usage.\",\n \"goalBudgetGate\": \"Token Budget Gate\",\n \"goalBudgetGateDesc\": \"Enforces a token spending limit for the goal session. Stops the agent when budget is exceeded.\",\n \"goalRubricGate\": \"Goal Completion Rubric\",\n \"goalRubricGateDesc\": \"Evaluates whether the goal has been achieved by checking the session status.\",\n \"goalDoomGate\": \"Repetition Protection\",\n \"goalDoomGateDesc\": \"Detects repetitive patterns during goal execution and triggers intervention.\",\n \"missionProgressGate\": \"Mission Progress Gate\",\n \"missionProgressGateDesc\": \"Tracks PRD user story completion. Continues until all stories pass or max iterations reached.\",\n \"missionIterBypass\": \"Iteration Bypass\",\n \"missionIterBypassDesc\": \"Temporarily lifts the ReAct iteration limit during mission execution to allow long-running phases.\",\n \"customLoopTitle\": \"Custom Loop Modes\",\n \"customLoopDesc\": \"Create your own loop modes with custom gate combinations. Coming soon.\",\n \"autoGenerateSessionTitle\": \"Auto-generate session titles\",\n \"autoGenerateSessionTitleTooltip\": \"After the first user message in a new chat, run a short background LLM call to replace the truncated placeholder with a concise title. Costs one extra short LLM call per new chat. Turn off to keep the placeholder and skip the spend.\",\n \"shellCommandTimeout\": \"Shell Command Timeout (s)\",\n \"shellCommandTimeoutTooltip\": \"Default timeout in seconds for the execute_shell_command tool. The LLM can still override this per-call via the timeout parameter. Useful for long-running tasks like compilation or data processing.\",\n \"shellCommandTimeoutPlaceholder\": \"Enter timeout\",\n \"shellCommandTimeoutRequired\": \"Shell command timeout is required\",\n \"shellCommandTimeoutMin\": \"Shell command timeout must be at least 1 second\",\n \"shellCommandExecutable\": \"Shell Executable\",\n \"shellCommandExecutableTooltip\": \"Path to the shell used by execute_shell_command. Linux/macOS: e.g. /bin/bash, /bin/zsh. Windows: supports powershell.exe, pwsh.exe, or POSIX-like shells such as Git Bash. When empty, falls back to the $SHELL environment variable, then to the platform default (/bin/sh on Unix, cmd.exe on Windows).\",\n \"shellCommandExecutablePlaceholder\": \"e.g. /bin/bash or powershell.exe (empty = auto-detect)\",\n \"maxInputLength\": \"Max Input Length\",\n \"maxInputLengthTooltip\": \"Maximum input length (tokens) for the model context window\",\n \"maxInputLengthPlaceholder\": \"Enter max input length\",\n \"maxInputLengthRequired\": \"Max input length is required\",\n \"maxInputLengthMin\": \"Max input length must be at least 1000\",\n \"contextCompactRatio\": \"Compaction Threshold Ratio\",\n \"contextCompactRatioTooltip\": \"Compaction is triggered when the context length reaches this fraction of the max context length\",\n \"contextCompactRatioRequired\": \"Context compact ratio is required\",\n \"contextCompactThreshold\": \"Compaction Threshold (tokens)\",\n \"contextCompactThresholdTooltip\": \"Auto-calculated from max context length × compaction ratio. Compaction triggers when context exceeds this value.\",\n \"contextCompactThresholdPlaceholder\": \"Auto-calculated\",\n \"contextCompactReserveRatio\": \"Reserve Threshold Ratio\",\n \"contextCompactReserveRatioTooltip\": \"Calculates the target recent-tail budget. Scroll also applies a minimum recent budget and a 40K cap, and always keeps the complete active turn.\",\n \"contextCompactReserveRatioRequired\": \"Context reserve ratio is required\",\n \"contextCompactReserveThreshold\": \"Reserve Threshold (tokens)\",\n \"contextCompactReserveThresholdTooltip\": \"The effective target used to split the recent tail. This is a soft target; boundary messages and the complete active turn may increase the retained amount.\",\n \"contextCompactReserveThresholdPlaceholder\": \"Auto-calculated\",\n \"toolResultCompactRecentN\": \"Recent Tool Results Range\",\n \"toolResultCompactRecentNTooltip\": \"Number of recent tool-result-bearing messages that use the larger recent preview limit in Native context mode\",\n \"toolResultCompactRecentNRequired\": \"Recent tool results range is required\",\n \"toolResultCompactOldThreshold\": \"Older Tool Result Preview Bytes\",\n \"toolResultCompactOldThresholdTooltip\": \"Maximum preview bytes for older tool results in Native context mode.\",\n \"toolResultCompactOldThresholdPlaceholder\": \"Enter preview byte limit\",\n \"toolResultCompactOldThresholdRequired\": \"Older tool result preview bytes is required\",\n \"toolResultCompactRecentThreshold\": \"Recent Tool Result Preview Bytes\",\n \"toolResultCompactRecentThresholdTooltip\": \"Maximum preview bytes before a tool result enters agent context. In Scroll mode, this also limits each recall_history page and read_file response.\",\n \"toolResultCompactRecentThresholdPlaceholder\": \"Enter preview byte limit\",\n \"toolResultCompactRecentThresholdRequired\": \"Recent tool result preview bytes is required\",\n \"toolResultCompactRetentionDays\": \"Complete Tool Result Retention Days\",\n \"toolResultCompactRetentionDaysTooltip\": \"Independent retention period for complete tool result artifacts. After cleanup, history.db may still retain a bounded preview.\",\n \"toolResultCompactRetentionDaysRequired\": \"Complete tool result retention days is required\",\n \"historyRetentionDays\": \"History Retention Days\",\n \"historyRetentionDaysTooltip\": \"Days of durable chat history to keep. Older rows are purged automatically on startup and on agent teardown.\",\n \"historyRetentionDaysRequired\": \"History retention days is required\",\n \"historyRetentionDaysForeverWarning\": \"Set to 0 keeps history forever — storage will grow without bound.\",\n \"historyRetentionDaysLargeWarning\": \"A large retention window may consume significant storage.\",\n \"exemptFileExtensions\": \"Exempt File Extensions\",\n \"exemptFileExtensionsTooltip\": \"In Native context mode, read_file results for these extensions always use the larger recent preview limit\",\n \"exemptFileExtensionsPlaceholder\": \".md, .txt, .json\",\n \"exemptToolNames\": \"Exempt Tool Names\",\n \"exemptToolNamesTooltip\": \"In Native context mode, results from these tools always use the larger recent preview limit\",\n \"exemptToolNamesPlaceholder\": \"chat_with_agent, list_agents\",\n \"language\": \"Agent Language\",\n \"languageTooltip\": \"Language for agent persona files (SOUL.md, AGENTS.md, etc.). Changing this will re-copy MD files in the selected language.\",\n \"languageConfirmTitle\": \"Change Agent Language\",\n \"languageConfirmContent\": \"Changing the language will overwrite the following files with the new language version:\\n\\n SOUL.md, AGENTS.md, PROFILE.md, MEMORY.md, BOOTSTRAP.md, HEARTBEAT.md\\n\\nIf you have customized any of these files, please back them up first. Your other files will not be affected.\",\n \"languageConfirmOk\": \"Change Language\",\n \"languageSaveSuccess\": \"Language updated successfully\",\n \"languageSaveSuccessWithFiles\": \"Language updated, {{count}} MD file(s) copied\",\n \"languageSaveFailed\": \"Failed to update language\",\n \"saveSuccess\": \"Configuration saved successfully\",\n \"saveFailed\": \"Failed to save configuration\",\n \"timezone\": \"User Timezone\",\n \"timezoneTooltip\": \"Used for scheduled tasks, time display, and agent context. Defaults to your system timezone.\",\n \"timezoneRequired\": \"Please select a timezone\",\n \"selectTimezone\": \"Select timezone\",\n \"timezoneSaveSuccess\": \"Timezone updated successfully\",\n \"timezoneSaveFailed\": \"Failed to update timezone\",\n \"loadFailed\": \"Failed to load configuration\",\n \"historyMaxLength\": \"Max History Length\",\n \"historyMaxLengthTooltip\": \"Maximum output length (characters) for the /history command\",\n \"historyMaxLengthRequired\": \"Max history length is required\",\n \"historyMaxLengthMin\": \"Max history length must be at least 1000\",\n \"historyMaxLengthPlaceholder\": \"Enter max length\",\n \"contextCompactTitle\": \"Context Compact Config\",\n \"contextCompactEnabled\": \"Enable Context Compaction\",\n \"contextCompactEnabledTooltip\": \"Automatically compact the context to prevent context window overflow\",\n \"tokenCountModel\": \"Token Count Model\",\n \"tokenCountModelTooltip\": \"Model name used for precise token counting; set to \\\"default\\\" to use built-in estimation\",\n \"tokenCountModelPlaceholder\": \"Model name or default\",\n \"tokenCountUseMirror\": \"Use HuggingFace Mirror\",\n \"tokenCountUseMirrorTooltip\": \"Use a HuggingFace mirror site when downloading tokenizers\",\n \"tokenCountEstimateDivisor\": \"byte/Token Estimate Divisor\",\n \"tokenCountEstimateDivisorTooltip\": \"Divisor for byte-based token estimation (tokens ≈ byte_length / divisor). Recommended value: 3.75 ~ 4.\",\n \"tokenCountEstimateDivisorRequired\": \"Estimate divisor is required\",\n \"tokenCountEstimateDivisorMin\": \"Estimate divisor must be between 2 and 5\",\n \"toolResultCompactTitle\": \"Tool Result Compact Config\",\n \"toolResultCompactEnabled\": \"Enable Tool Result Compaction\",\n \"toolResultCompactEnabledTooltip\": \"Compact oversized tool call results to save context space\",\n \"memorySummaryTitle\": \"Long-term Memory Config\",\n \"memorySummaryEnabled\": \"Activate Memory Summary\",\n \"memorySummaryEnabledTooltip\": \"Automatically generate and save memory summaries during context compaction\",\n \"dreamCronEnabled\": \"Enable Dream\",\n \"dreamCronEnabledTooltip\": \"Enable the scheduled dream-based memory optimization job\",\n \"dreamCron\": \"Dream Schedule\",\n \"dreamCronTooltip\": \"Cron expression for periodically optimizing long-term memories through dream state. Scheduled runs start after a random delay of 0–60 seconds to avoid simultaneous calls; use the switch above to enable or disable it\",\n \"dreamCronPlaceholder\": \"0 23 * * *\",\n \"dreamCronRequired\": \"Enter a cron expression when Dream is enabled\",\n \"dreamCronInvalid\": \"Enter a valid 5-field cron expression\",\n \"autoMemorySearch\": \"Auto Memory Search (Beta)\",\n \"autoMemorySearchTooltip\": \"Auto memory search to run on every conversation turn. Each query will automatically invoke memory search capabilities, helping QwenPaw understand you better — but this increases token consumption.\",\n \"autoMaxResults\": \"Auto Search Max Results\",\n \"autoMaxResultsTooltip\": \"Maximum number of results returned when auto memory search is enabled\",\n \"autoMaxResultsRequired\": \"Max results is required\",\n \"autoMaxResultsMin\": \"Max results must be at least 1\",\n \"embeddingConfigTitle\": \"Embedding Model Config\",\n \"embeddingBackend\": \"Embedding SDK Type\",\n \"embeddingBackendTooltip\": \"SDK type used to call the embedding model\",\n \"embeddingBackendPlaceholder\": \"Select an SDK type\",\n \"embeddingApiKey\": \"API Key\",\n \"embeddingApiKeyTooltip\": \"API key for the embedding provider; leave blank if no authentication is required\",\n \"embeddingApiKeyPlaceholder\": \"Leave blank if not required\",\n \"embeddingBaseUrl\": \"Base URL\",\n \"embeddingBaseUrlTooltip\": \"Base URL of the embedding API. For OpenAI-compatible backends, this custom endpoint is optional; API Key and Model Name are required.\",\n \"embeddingBaseUrlPlaceholder\": \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n \"embeddingHost\": \"Host\",\n \"embeddingHostTooltip\": \"Ollama host URL. Leave blank to use Ollama's default host.\",\n \"embeddingHostPlaceholder\": \"http://localhost:11434\",\n \"embeddingModelName\": \"Model Name\",\n \"embeddingModelNameTooltip\": \"Embedding model name. Required for every embedding backend.\",\n \"embeddingModelNamePlaceholder\": \"text-embedding-v4\",\n \"embeddingEnableCache\": \"Enable Embedding Cache\",\n \"embeddingEnableCacheTooltip\": \"Cache embedding results to reduce repeated computation\",\n \"embeddingMaxCacheSize\": \"Max Cache Size\",\n \"embeddingMaxCacheSizeTooltip\": \"Maximum number of entries in the embedding cache\",\n \"embeddingMaxCacheSizeRequired\": \"Max cache size is required\",\n \"embeddingUseDimensions\": \"Use Custom Dimensions\",\n \"embeddingUseDimensionsTooltip\": \"Use custom embedding dimensions (only supported by some models)\",\n \"embeddingDimensions\": \"Embedding Dimensions\",\n \"embeddingDimensionsTooltip\": \"Number of embedding dimensions; takes effect when custom dimensions are enabled\",\n \"embeddingDimensionsRequired\": \"Embedding dimensions is required\",\n \"embeddingDimensionsMin\": \"Dimensions must be at least 1\",\n \"embeddingMaxInputLength\": \"Max Input Length\",\n \"embeddingMaxInputLengthTooltip\": \"Maximum input length (tokens) for a single embedding request\",\n \"embeddingMaxInputLengthRequired\": \"Max input length is required\",\n \"embeddingMaxBatchSize\": \"Max Batch Size\",\n \"embeddingMaxBatchSizeTooltip\": \"Maximum number of items in a single batch embedding request\",\n \"embeddingMaxBatchSizeRequired\": \"Max batch size is required\",\n \"embeddingRestartWarning\": \"Embedding model config does not support hot reload. Save and restart the service for changes to take effect.\",\n \"toolExecutionLevelTitle\": \"Tool Execution Security\",\n \"toolExecutionLevel\": {\n \"title\": \"Tool Execution Security\",\n \"alertMessage\": \"Configure tool call approval policy to control the security level when agents execute tools\",\n \"strict\": \"Strict Mode\",\n \"strictDesc\": \"All tool calls require approval, highest security level\",\n \"smart\": \"Smart Mode\",\n \"smartDesc\": \"Low-risk tools auto-approved, medium+ risk tools require approval\",\n \"auto\": \"Auto Mode\",\n \"autoDesc\": \"Only tools explicitly marked for approval require approval (default)\",\n \"off\": \"Off Mode\",\n \"offDesc\": \"Disable all tool approvals, all tools execute automatically\"\n },\n \"saveLevelSuccess\": \"Tool execution security level saved\",\n \"saveLevelFailed\": \"Failed to save tool execution security level\",\n \"offloadPolicy\": {\n \"title\": \"Tool Background Execution\",\n \"alertMessage\": \"This is a global setting (settings.json), not per-agent. It controls the default action when a tool reaches its offload deadline. Users can override it from the tool control panel while a tool is running.\",\n \"keepForeground\": \"Keep Foreground\",\n \"keepForegroundDesc\": \"After the countdown expires, the tool continues running in the foreground without auto-offloading. Suitable for scenarios requiring real-time output monitoring.\",\n \"offload\": \"Auto Offload to Background\",\n \"offloadDesc\": \"After the countdown expires, the tool is automatically moved to background execution, allowing the Agent to continue processing other tasks. Suitable for long-running tools.\",\n \"saveFailed\": \"Failed to save policy\"\n },\n \"maxContextLength\": \"Max Context Length\",\n \"maxContextLengthTooltip\": \"Maximum token count for the model context window, determining how much history fits in each request\",\n \"maxContextLengthRequired\": \"Max context length is required\",\n \"maxContextLengthMin\": \"Max context length must be at least 1000\",\n \"maxContextLengthPlaceholder\": \"Enter max context length\",\n \"embeddingActivationNote\": \"Embedding is enabled when the selected backend has the required fields.\",\n \"embeddingEnableHint\": \"Embedding is enabled when the selected backend has the required fields: Model Name plus API Key for OpenAI-compatible or Gemini backends; Model Name for Ollama.\",\n \"rebuildMemoryIndex\": \"Rebuild Memory Index\",\n \"rebuildMemoryIndexWarning\": \"This clears and rebuilds the memory search index and may consume significant CPU and memory. Use it only to repair a damaged index or abnormal search results.\",\n \"rebuildMemoryIndexConfirmTitle\": \"Rebuild the memory index?\",\n \"rebuildMemoryIndexConfirm\": \"CPU and memory usage may increase significantly during the rebuild. This is not recommended unless the index is damaged or needs repair.\",\n \"rebuildMemoryIndexSuccess\": \"Memory index rebuild completed\",\n \"rebuildMemoryIndexFailed\": \"Memory index rebuild failed: {{error}}\",\n \"toolResultCompactRecentGtOld\": \"Recent tool result preview bytes must be greater than or equal to compact preview bytes\",\n \"lightContextTitle\": \"Context Management\",\n \"remeLightMemoryTitle\": \"Long-term Memory\",\n \"dialogPath\": \"Dialog Storage Path\",\n \"dialogPathTooltip\": \"Relative path for dialog persistence storage (relative to working_dir)\",\n \"dialogPathPlaceholder\": \"dialog\",\n \"contextCompactCollapseLabel\": \"Context Compaction\",\n \"toolResultPruningCollapseLabel\": \"Tool Result Pruning\",\n \"visualCompactCollapseLabel\": \"Visual Compact\",\n \"visualCompactDescription\": \"Compact older, longer context into visual pages to reduce input tokens. Recent conversation remains text.\",\n \"visualCompactEnabled\": \"Enable Visual Compact\",\n \"visualCompactEffort\": \"Compression intensity\",\n \"visualCompactLow\": \"Low\",\n \"visualCompactMedium\": \"Medium\",\n \"visualCompactHigh\": \"High\",\n \"visualCompactLowDescription\": \"Compress less content and prioritize readability.\",\n \"visualCompactMediumDescription\": \"Balance token savings with visual readability.\",\n \"visualCompactHighDescription\": \"Compress more eligible context and prioritize token savings.\",\n \"visualCompactQualityNote\": \"Higher compression does not necessarily improve answer quality.\",\n \"visualCompactCapabilityNote\": \"Only takes effect with models that support image input.\",\n \"autoMemorySearchCollapseLabel\": \"Auto Memory Search\",\n \"embeddingConfigCollapseLabel\": \"Embedding Model Config\",\n \"summarizeWhenCompact\": \"Memory on Compact\",\n \"summarizeWhenCompactTooltip\": \"Automatically generate and save memory during context compaction\",\n \"inboxPushEnabled\": \"Memory Result Inbox Notifications\",\n \"inboxPushEnabledTooltip\": \"Push auto-memory, auto-dream, and auto-resource results to the inbox. Disable this to reduce inbox messages without stopping the memory jobs.\",\n \"autoMemoryInterval\": \"Auto Memory Interval\",\n \"autoMemoryIntervalTooltip\": \"Trigger auto memory every N user queries. 0 disables periodic auto memory. 1 means auto memory after every query. Recommended: 2-5; setting 1 requires attention to high token usage.\",\n \"autoMemoryIntervalPlaceholder\": \"Enter 0 to disable periodic auto memory\",\n \"autoMemoryIntervalRequired\": \"Auto memory interval is required\",\n \"autoMemoryIntervalMin\": \"Auto memory interval must be at least 0\",\n \"contextManagerBackend\": \"Context Manager Backend\",\n \"contextManagerBackendTooltip\": \"Backend type for the context manager. Currently only Light is supported.\",\n \"contextStrategy\": \"Context Strategy\",\n \"contextStrategyTooltip\": \"How the agent manages long context. Scroll (recommended) persists evicted turns to a durable history store and recalls them on demand; Native uses in-place compression. Restart QwenPaw for changes to take effect.\",\n \"contextStrategyScroll\": \"Scroll\",\n \"contextStrategyNative\": \"Native\",\n \"backendRestartWarning\": \"Switching backends does not support hot reload. Save and restart QwenPaw for changes to take effect.\",\n \"adbpgMemoryTitle\": \"ADBPG Long-term Memory\",\n \"adbpgConfig\": {\n \"title\": \"ADBPG Memory Configuration\",\n \"restBaseUrl\": \"REST Base URL\",\n \"restApiKey\": \"REST API Key\",\n \"memoryIsolation\": \"Memory Isolation (per-agent)\",\n \"searchTimeout\": \"Search Timeout\"\n }\n },\n \"tools\": {\n \"title\": \"Built-in Tools\",\n \"description\": \"Manage built-in tools and their enabled status. Disabled tools will not be available to the agent.\",\n \"emptyState\": \"No tools configured\",\n \"loadError\": \"Failed to load tools\",\n \"enableSuccess\": \"Tool enabled successfully\",\n \"disableSuccess\": \"Tool disabled successfully\",\n \"toggleError\": \"Failed to toggle tool status\",\n \"enableAll\": \"Enable All\",\n \"disableAll\": \"Disable All\",\n \"enableAllSuccess\": \"All tools enabled\",\n \"disableAllSuccess\": \"All tools disabled\",\n \"allEnabled\": \"All tools are already enabled\",\n \"allDisabled\": \"All tools are already disabled\",\n \"asyncExecution\": \"Async Execution\",\n \"asyncExecutionEnabled\": \"Async execution enabled\",\n \"asyncExecutionDisabled\": \"Async execution disabled\",\n \"browserUnifiedMode\": \"Unified Browser (Beta)\",\n \"browserLegacyMode\": \"Legacy Compatibility Mode\",\n \"browserUnifiedModeButton\": \"New (Beta)\",\n \"browserLegacyModeButton\": \"Legacy (Compatible)\",\n \"browserUnifiedDescription\": \"Use the new unified browser capabilities.\",\n \"browserLegacyDescription\": \"Use the original browser implementation for legacy behavior.\",\n \"browserRestartPending\": \"The service will switch to {{mode}} after restart.\",\n \"configure\": \"Configure\",\n \"configured\": \"Configured\",\n \"requiresConfig\": \"Requires configuration\",\n \"configSaved\": \"Configuration saved\",\n \"configSaveError\": \"Failed to save configuration\",\n \"active\": \"active\",\n \"available\": \"Available\",\n \"noEnabled\": \"No tools enabled\",\n \"goEnableBtn\": \"Go enable\",\n \"configureAction\": \"Configure →\",\n \"enableAction\": \"Enable →\"\n },\n \"modelConfig\": {\n \"promptTitle\": \"LLM Model Required — Select in Chat After Configuration\",\n \"promptMessage\": \"Chat requires an LLM model to function. Please configure a model first, then select it from the model selector at the top of the Chat page.\",\n \"configureButton\": \"Configure Model\",\n \"skipButton\": \"Skip\",\n \"chatDisabledTitle\": \"Chat Disabled\",\n \"chatDisabledMessage\": \"Chat functionality is disabled because no model is configured. Please configure a model to enable chat.\",\n \"configureNow\": \"Configure Model\",\n \"modelNotConfigured\": \"Please configure a model in Settings before using chat\"\n },\n \"header\": {\n \"changelog\": \"Changelog\",\n \"docs\": \"Doc\",\n \"tutorial\": \"Tutorial\",\n \"featureDemos\": \"Feature Demos\",\n \"faq\": \"FAQ\",\n \"github\": \"GitHub\",\n \"resources\": \"Documentation\",\n \"updateAvailable\": \"Update Available\"\n },\n \"theme\": {\n \"dark\": \"Dark\",\n \"light\": \"Light\",\n \"system\": \"System\"\n },\n \"chat\": {\n \"modelSelectTooltip\": \"Select model\",\n \"newChatTooltip\": \"New chat\",\n \"searchTooltip\": \"Search all chats\",\n \"chatHistoryTooltip\": \"Chat history\",\n \"wideModeTooltip\": \"Wide mode\",\n \"normalModeTooltip\": \"Normal mode\",\n \"allChats\": \"All Chats\",\n \"createNewChat\": \"Create New Chat\",\n \"pinDrawer\": \"Pin\",\n \"unpinDrawer\": \"Unpin\",\n \"statusIdle\": \"Idle\",\n \"statusInProgress\": \"In progress\",\n \"disclaimer\": \"Works for you, grows with you\",\n \"greeting\": \"Hello, how can I help you today?\",\n \"description\": \"I am a helpful assistant that can help you with your questions.\",\n \"prompt1\": \"Let's start a new journey!\",\n \"prompt2\": \"Can you tell me what skills you have?\",\n \"inputPlaceholder\": \"\\\"↑↓\\\" for message navigation · \\\"/\\\" for quick commands (\\\"/approve\\\" or \\\"/deny\\\" during approvals)\",\n \"longTextUploadPrompt\": \"Please read the uploaded prompt file and answer it.\",\n \"emptyOutputError\": \"The assistant returned an empty response.\",\n \"commands\": {\n \"clear\": {\n \"description\": \"Clear the conversation context\"\n },\n \"compact\": {\n \"description\": \"Compact the conversation context; optional instruction supported\"\n },\n \"approve\": {\n \"description\": \"Approve the pending tool action and continue execution\"\n },\n \"deny\": {\n \"description\": \"Deny the pending tool action\"\n },\n \"mission\": {\n \"description\": \"Mission mode — Decomposes tasks into sub-agents with isolated context\"\n },\n \"skills\": {\n \"description\": \"List chat-available skills and expose explicit skill commands\"\n },\n \"agents\": {\n \"description\": \"List agents available to the current runtime\"\n },\n \"review\": {\n \"description\": \"Review uncommitted workspace changes with Codex\"\n },\n \"status\": {\n \"description\": \"Show the current agent account, model, and workspace\"\n },\n \"plan\": {\n \"description\": \"Create a structured plan to decompose the task into subtasks\"\n },\n \"goal\": {\n \"description\": \"Goal loop — Agent pursues a goal continuously, auto-evaluates via Rubric\"\n }\n },\n \"attachments\": {\n \"tooltip\": \"Supports documents, images, videos, audio and more formats\",\n \"tooltipImageOnly\": \"Current model supports image upload only\",\n \"tooltipNoMultimodal\": \"No multimodal capability detected for current model, images or videos may not be processed correctly\",\n \"fileSizeLimit\": \"File size limit: {{limit}}MB\",\n \"fileSizeExceeded\": \"File size exceeds {{limit}}MB limit. Current file: {{size}}MB\",\n \"multimodalDisabled\": \"Current model does not support file upload\",\n \"multimodalWarning\": \"No multimodal capability detected for current model, images or videos may not be processed correctly\",\n \"imageOnlyAccept\": \"Current model only supports image files\",\n \"imageOnlyWarning\": \"Current model only supports images, non-image files like videos may not be processed correctly\"\n },\n \"rateLimitHit\": \"The current model is rate-limited. Try switching to another free model.\",\n \"turnUsagePopover\": {\n \"ariaLabel\": \"View turn token and context usage\",\n \"turn\": \"This turn\",\n \"turnEstimated\": \"This turn ~\",\n \"tok\": \"tok\",\n \"inOut\": \"in {{inTok}} · out {{outTok}}\",\n \"contextLabel\": \"Context window:\",\n \"manageContext\": \"Manage context\",\n \"compact\": \"Compact\",\n \"new\": \"New chat\"\n },\n \"rateLimitMessage\": \"Rate limit reached. Switch to another free model:\",\n \"rateLimitSwitched\": \"Switched to {{model}}\",\n \"speech\": {\n \"startRecording\": \"Start voice recording (Ctrl+Shift+M)\",\n \"stopRecording\": \"Stop recording (Ctrl+Shift+M)\",\n \"transcribing\": \"Transcribing...\",\n \"transcriptionDisabled\": \"Transcription is disabled. Configure a provider in Settings > Voice Transcription.\",\n \"transcriptionFailed\": \"Voice transcription failed\",\n \"microphoneError\": \"Could not access microphone\",\n \"fileTooLarge\": \"Recording file is too large ({{size}}MB). Maximum allowed: {{limit}}MB\",\n \"recordingTooLong\": \"Recording exceeds duration limit ({{limit}}s)\"\n },\n \"search\": {\n \"title\": \"Search Chat\",\n \"tooltip\": \"Search current chat\",\n \"placeholder\": \"Search messages...\",\n \"noResults\": \"No results found\",\n \"resultsCount\": \"Found {{count}} result(s)\",\n \"searching\": \"Searching {{progress}}...\",\n \"loading\": \"Loading...\",\n \"userMessage\": \"User\",\n \"assistantMessage\": \"Assistant\",\n \"titleMatch\": \"Title\"\n },\n \"contextMenu\": {\n \"open\": \"Open\",\n \"rename\": \"Rename\",\n \"pin\": \"Pin\",\n \"unpin\": \"Unpin\",\n \"delete\": \"Delete\"\n },\n \"queue\": {\n \"title\": \"Message Queue\",\n \"paused\": \"Paused\",\n \"sendFailed\": \"Send failed\",\n \"resume\": \"Resume queue\",\n \"pause\": \"Pause queue\",\n \"clear\": \"Clear queue\",\n \"dragSort\": \"Drag to reorder\",\n \"edit\": \"Edit\",\n \"retry\": \"Retry\",\n \"skip\": \"Skip\",\n \"interruptAndSend\": \"Interrupt & send\",\n \"delete\": \"Delete\",\n \"file\": \"File\",\n \"noChatWarning\": \"Please send a message first to create a conversation before using the queue\",\n \"queueFull\": \"Queue is full (max {{max}} items), please send or remove some messages first\",\n \"otherTabOwner\": \"This tab queues only; sending is handled by another tab\"\n },\n \"group\": {\n \"pinned\": \"Pinned\",\n \"today\": \"Today\",\n \"week\": \"Within 7 days\",\n \"month\": \"Within 30 days\",\n \"older\": \"Earlier\"\n },\n \"sessionPanel\": {\n \"searchConversations\": \"Search conversations…\",\n \"noResults\": \"No results\",\n \"noConversations\": \"No conversations yet\",\n \"loadingConversations\": \"Loading…\"\n },\n \"conversationHistory\": \"Conversation History\",\n \"history\": {\n \"empty\": \"No chat history\"\n },\n \"sessionGroup\": {\n \"today\": \"Today\",\n \"recent\": \"Last 7 days\",\n \"earlier\": \"Earlier\"\n }\n },\n \"security\": {\n \"parent\": \"Settings\",\n \"security\": \"Security\",\n \"title\": \"Security\",\n \"description\": \"Manage security features for tools and skills.\",\n \"toolGuardTitle\": \"Tool Guard\",\n \"toolGuardDescription\": \"Configure security scanning for tool calls. Dangerous operations will require your explicit approval before execution.\",\n \"enabled\": \"Enable Tool Guard\",\n \"enabledTooltip\": \"When enabled, tool calls are scanned for dangerous patterns before execution\",\n \"sandboxEnabled\": \"Enable Sandbox Execution\",\n \"sandboxEnabledTooltip\": \"When enabled, shell commands with no matching rule run silently inside the sandbox. When disabled, such commands run directly without the sandbox (no approval prompt). Either way, secret-file reads and dangerous commands (e.g. rm -rf /, sudo) are still blocked or require approval.\",\n \"sandboxDegradedWarning\": \"Sandbox is configured as enabled, but QwenPaw is not running as administrator. The sandbox will be inactive for this session. To use the sandbox, restart QwenPaw with administrator privileges.\",\n \"sandboxUnelevatedWarning\": \"Sandbox is running in non-administrator mode with limited isolation\",\n \"sandboxUnelevatedDescription\": \"The unelevated sandbox provides incomplete protection and carries additional security risks compared to the full sandbox. For maximum security, it is recommended to run QwenPaw as administrator. To do so: right-click the QwenPaw shortcut → Run as administrator, or open an elevated terminal and run `qwenpaw app`.\",\n \"guardedTools\": \"Guarded Tools\",\n \"guardedToolsTooltip\": \"Tools that require approval when dangerous patterns are detected. Leave empty to use the built-in default set.\",\n \"guardedToolsPlaceholder\": \"Select tools or type custom tool names\",\n \"deniedTools\": \"Denied Tools\",\n \"deniedToolsTooltip\": \"Tools that are always rejected without asking for approval\",\n \"deniedToolsPlaceholder\": \"Select tools to always deny\",\n \"saveSuccess\": \"Tool guard settings saved\",\n \"saveFailed\": \"Failed to save tool guard settings\",\n \"loadFailed\": \"Failed to load tool guard settings\",\n \"rules\": {\n \"title\": \"Detection Rules\",\n \"description\": \"Rules define regex patterns that trigger security alerts. Built-in rules can be disabled; custom rules can be added, edited, or deleted.\",\n \"id\": \"Rule ID\",\n \"severity\": \"Severity\",\n \"descriptionCol\": \"Description\",\n \"source\": \"Source\",\n \"actions\": \"Actions\",\n \"builtin\": \"Built-in\",\n \"custom\": \"Custom\",\n \"enable\": \"Enable\",\n \"disable\": \"Disable\",\n \"edit\": \"Edit\",\n \"delete\": \"Delete\",\n \"add\": \"Add Rule\",\n \"addTitle\": \"Add Custom Rule\",\n \"editTitle\": \"Edit Custom Rule\",\n \"ruleId\": \"Rule ID\",\n \"ruleIdRequired\": \"Rule ID is required\",\n \"duplicateId\": \"A rule with this ID already exists\",\n \"tools\": \"Target Tools\",\n \"toolsPlaceholder\": \"Leave empty to match all tools\",\n \"params\": \"Target Parameters\",\n \"paramsPlaceholder\": \"Leave empty to match all parameters\",\n \"severityLabel\": \"Severity\",\n \"categoryLabel\": \"Category\",\n \"patterns\": \"Regex Patterns\",\n \"patternsRequired\": \"At least one pattern is required\",\n \"patternsTooltip\": \"One regex per line. Matched against tool parameter values (case-insensitive).\",\n \"excludePatterns\": \"Exclude Patterns\",\n \"excludePatternsTooltip\": \"One regex per line. If any matches, the rule is skipped for that value.\",\n \"descriptionLabel\": \"Description\",\n \"descriptionPlaceholder\": \"What does this rule detect?\",\n \"remediationLabel\": \"Remediation\",\n \"remediationPlaceholder\": \"Suggested action when this rule triggers\",\n \"preview\": \"Preview\",\n \"previewTitle\": \"Rule Details\",\n \"actionLabel\": \"Action\",\n \"actionApproval\": \"Require Approval\",\n \"autoDeny\": \"Auto Deny\",\n \"autoDenyTooltip\": \"When enabled, tool calls matching this rule will be automatically denied without requiring manual approval\",\n \"autoDenyEnable\": \"Enable auto deny for this rule\",\n \"autoDenyDisable\": \"Disable auto deny for this rule\",\n \"allTools\": \"All tools\",\n \"allParams\": \"All parameters\",\n \"descriptions\": {\n \"TOOL_CMD_CONTROL_CHARS\": \"Command contains non-printable control characters that could bypass security checks\",\n \"TOOL_CMD_DANGEROUS_MV\": \"Detects 'mv' command that may move or overwrite files unexpectedly\",\n \"TOOL_CMD_DANGEROUS_RM\": \"Detects 'rm' command that may cause data loss\",\n \"TOOL_CMD_DOS_FORK_BOMB\": \"Detects classic Bash fork bombs and mass process termination\",\n \"TOOL_CMD_FIND_DELETE\": \"Detects 'find -delete' command that may cause data loss\",\n \"TOOL_CMD_FS_DESTRUCTION\": \"Detects low-level disk formatting or wiping commands\",\n \"TOOL_CMD_IFS_INJECTION\": \"Command uses $IFS variable which could bypass security validation\",\n \"TOOL_CMD_JQ_FILE_FLAGS\": \"jq command uses flags that could read arbitrary files or execute external code\",\n \"TOOL_CMD_JQ_SYSTEM\": \"jq command contains system() function which can execute arbitrary shell commands\",\n \"TOOL_CMD_OBFUSCATED_EXEC\": \"Detects execution of base64 encoded strings passed directly to a shell interpreter\",\n \"TOOL_CMD_PIPE_TO_SHELL\": \"Detects 'curl | bash' patterns used to download and immediately execute remote payloads\",\n \"TOOL_CMD_PRIVILEGE_ESCALATION\": \"Detects privilege escalation attempts using sudo, su, doas, pkexec, or runas\",\n \"TOOL_CMD_PROCESS_KILL\": \"Detects process termination commands that may kill critical processes\",\n \"TOOL_CMD_PROC_ENVIRON\": \"Command accesses /proc/*/environ which could expose sensitive environment variables\",\n \"TOOL_CMD_REVERSE_SHELL\": \"Detects attempts to establish reverse shells or unauthorized network tunnels\",\n \"TOOL_CMD_SERVICE_RESTART\": \"Detects service management commands that can disrupt system services\",\n \"TOOL_CMD_SYSTEM_REBOOT\": \"Detects system reboot or shutdown commands that will terminate the host system\",\n \"TOOL_CMD_SYSTEM_TAMPERING\": \"Detects access to cron jobs, SSH keys, or sudo permissions (including reads and modifications)\",\n \"TOOL_CMD_UNICODE_WHITESPACE\": \"Command contains Unicode whitespace characters that could cause parsing inconsistencies\",\n \"TOOL_CMD_UNSAFE_PERMISSIONS\": \"Detects global permission downgrades (chmod 777) or setting immutable flags\",\n \"TOOL_CMD_ZSH_DANGEROUS\": \"Command uses Zsh-specific builtins that can bypass security checks\"\n },\n \"categories\": {\n \"command_injection\": \"Command Injection\",\n \"code_execution\": \"Code Execution\",\n \"data_exfiltration\": \"Data Exfiltration\",\n \"path_traversal\": \"Path Traversal\",\n \"sensitive_file_access\": \"Sensitive File Access\",\n \"network_abuse\": \"Network Abuse\",\n \"credential_exposure\": \"Credential Exposure\",\n \"resource_abuse\": \"Resource Abuse\",\n \"privilege_escalation\": \"Privilege Escalation\",\n \"prompt_injection\": \"Prompt Injection\",\n \"other\": \"Other\"\n }\n },\n \"shellEvasion\": {\n \"title\": \"Shell Evasion Detection\",\n \"description\": \"Configure which shell evasion and obfuscation detection checks are enabled. These checks detect techniques that attempt to hide malicious intent from simpler regex-only detection.\",\n \"checks\": {\n \"command_substitution\": {\n \"name\": \"Command Substitution\",\n \"description\": \"Detects $(), backticks, process substitution, and other command substitution patterns\"\n },\n \"obfuscated_flags\": {\n \"name\": \"Obfuscated Flags\",\n \"description\": \"Detects ANSI-C quoting, locale quoting, and empty-quote flag obfuscation\"\n },\n \"backslash_escaped_whitespace\": {\n \"name\": \"Backslash Escaped Whitespace\",\n \"description\": \"Detects backslash-escaped spaces/tabs that can alter command parsing\"\n },\n \"backslash_escaped_operators\": {\n \"name\": \"Backslash Escaped Operators\",\n \"description\": \"Detects backslash before shell operators (;|&<>) that can hide command structure\"\n },\n \"newlines\": {\n \"name\": \"Hidden Newlines\",\n \"description\": \"Detects newlines and carriage returns that could separate hidden commands\"\n },\n \"comment_quote_desync\": {\n \"name\": \"Comment Quote Desync\",\n \"description\": \"Detects quote characters inside # comments that can desync quote tracking\"\n },\n \"quoted_newline\": {\n \"name\": \"Quoted Newline\",\n \"description\": \"Detects newlines inside quoted strings followed by #-prefixed lines that can hide arguments\"\n }\n }\n },\n \"fileGuard\": {\n \"title\": \"File Guard\",\n \"description\": \"Protect sensitive files and directories from being accessed by agent tools. Paths added here will be blocked across all tool calls.\",\n \"enableLabel\": \"Enable File Guard\",\n \"path\": \"Path\",\n \"actions\": \"Actions\",\n \"add\": \"Add\",\n \"inputPlaceholder\": \"Enter file or directory path (e.g. ~/.ssh/ or /etc/passwd)\",\n \"empty\": \"No custom sensitive paths configured\",\n \"directory\": \"Directory\",\n \"removeConfirm\": \"Remove this path from the protected list?\",\n \"duplicate\": \"This path is already in the list\",\n \"saveSuccess\": \"File guard settings saved\",\n \"saveFailed\": \"Failed to save file guard settings\",\n \"loadFailed\": \"Failed to load file guard settings\",\n \"allowPreviewOutsideWorkspace\": \"Allow Console Preview Outside Workspace\",\n \"allowPreviewOutsideWorkspaceDesc\": \"Allow previewing files outside the workspace directory in console. Sensitive file protection still applies.\"\n },\n \"allowNoAuthHosts\": {\n \"title\": \"Allow No Auth Hosts\",\n \"warningTitle\": \"Security Warning\",\n \"warningDescription\": \"IP addresses in this list can access API endpoints without authentication. By default, localhost (127.0.0.1 and ::1) are allowed for CLI access. Only add trusted IP addresses. WARNING: Adding untrusted IPs poses a serious security risk.\",\n \"ipAddress\": \"IP Address\",\n \"actions\": \"Actions\",\n \"add\": \"Add\",\n \"inputPlaceholder\": \"Enter IP address (e.g., 192.168.1.100 or ::1)\",\n \"empty\": \"No IP addresses configured\",\n \"default\": \"Default\",\n \"removeConfirm\": \"Remove this IP address from the whitelist?\",\n \"duplicate\": \"This IP address is already in the list\",\n \"invalidIP\": \"Invalid IP address format\",\n \"saveSuccess\": \"Allow no auth hosts settings saved\",\n \"saveFailed\": \"Failed to save allow no auth hosts settings\",\n \"loadFailed\": \"Failed to load allow no auth hosts settings\"\n },\n \"skillScanner\": {\n \"title\": \"Skill Scanner\",\n \"description\": \"Automatically scan skills for security threats before enabling or installing. Unsafe skills can be blocked or whitelisted.\",\n \"mode\": \"Scanner Mode\",\n \"modeTooltip\": \"Controls how the scanner handles unsafe skills: block, warn only, or disabled\",\n \"modeBlock\": \"Block\",\n \"modeWarn\": \"Warn Only\",\n \"modeOff\": \"Off\",\n \"timeout\": \"Scan Timeout (seconds)\",\n \"timeoutTooltip\": \"Maximum time to wait for a scan to complete (5-300 seconds)\",\n \"saveSuccess\": \"Skill scanner settings saved\",\n \"saveFailed\": \"Failed to save skill scanner settings\",\n \"scanAlerts\": {\n \"title\": \"Scan Alerts\",\n \"empty\": \"No security alerts\",\n \"clearAll\": \"Clear All\",\n \"clearConfirm\": \"Clear all scan alerts?\",\n \"skillName\": \"Skill\",\n \"action\": \"Action\",\n \"actionBlocked\": \"Blocked\",\n \"actionWarned\": \"Warned\",\n \"time\": \"Time\",\n \"actions\": \"Actions\",\n \"allowSkill\": \"Add to Whitelist\",\n \"remove\": \"Remove\",\n \"viewFindings\": \"View Details\"\n },\n \"whitelist\": {\n \"title\": \"Whitelist\",\n \"empty\": \"No skills are whitelisted\",\n \"skillName\": \"Skill\",\n \"addedAt\": \"Added At\",\n \"contentHash\": \"Content Hash\",\n \"actions\": \"Actions\",\n \"remove\": \"Remove\",\n \"removeConfirm\": \"Remove this skill from the whitelist?\",\n \"addSuccess\": \"Skill added to whitelist\",\n \"removeSuccess\": \"Skill removed from whitelist\",\n \"addFailed\": \"Failed to add skill to whitelist\",\n \"removeFailed\": \"Failed to remove skill from whitelist\",\n \"removeWillDisable\": \"The skill will also be disabled after removal.\",\n \"removeAndDisabled\": \"Skill removed from whitelist and disabled\"\n },\n \"scanError\": {\n \"title\": \"Security Issues Detected\",\n \"description\": \"The following security issues were found:\",\n \"warnDescription\": \"Security issues were found but the skill was still enabled (warn mode):\",\n \"goToWhitelist\": \"Go to Whitelist\",\n \"moreFindings\": \"{{count}} more issues not shown (see Security → Skill Scanner for the full list).\"\n }\n }\n },\n \"login\": {\n \"title\": \"Login to QwenPaw\",\n \"registerTitle\": \"Create Account\",\n \"firstUserHint\": \"Create your admin account to get started\",\n \"usernamePlaceholder\": \"Username\",\n \"passwordPlaceholder\": \"Password\",\n \"usernameRequired\": \"Please enter your username\",\n \"passwordRequired\": \"Please enter your password\",\n \"submit\": \"Login\",\n \"register\": \"Register\",\n \"failed\": \"Login failed, please check your credentials\",\n \"registerSuccess\": \"Registration successful\",\n \"registerFailed\": \"Registration failed\",\n \"authNotEnabled\": \"Authentication is not enabled\",\n \"logout\": \"Logout\",\n \"logoutSuccess\": \"Logged out successfully\",\n \"sessionExpired\": \"Session expired, please login again\",\n \"unauthorized\": \"Unauthorized, please login\"\n },\n \"account\": {\n \"title\": \"Account\",\n \"currentPassword\": \"Current Password\",\n \"currentPasswordRequired\": \"Please enter your current password\",\n \"newUsername\": \"New Username\",\n \"newUsernamePlaceholder\": \"Leave blank to keep current\",\n \"newPassword\": \"New Password\",\n \"newPasswordPlaceholder\": \"Leave blank to keep current\",\n \"confirmPassword\": \"Confirm New Password\",\n \"confirmPasswordPlaceholder\": \"Re-enter new password\",\n \"passwordMismatch\": \"Passwords do not match\",\n \"save\": \"Save Changes\",\n \"updateSuccess\": \"Account updated successfully\",\n \"updateFailed\": \"Failed to update account\",\n \"wrongPassword\": \"Current password is incorrect\",\n \"nothingToUpdate\": \"Please enter a new username or password\",\n \"passwordEmpty\": \"Password cannot be blank\",\n \"usernameEmpty\": \"Username cannot be blank\"\n },\n \"approval\": {\n \"title\": \"Security Approval Required\",\n \"ownerAgent\": \"Owner Agent\",\n \"executingAgent\": \"Executing Agent\",\n \"tool\": \"Tool\",\n \"severity\": \"Severity\",\n \"findings\": \"Findings\",\n \"parameters\": \"Parameters\",\n \"approve\": \"Approve\",\n \"deny\": \"Deny\",\n \"source\": \"Source\",\n \"builtinSource\": \"Built-in\",\n \"subSession\": \"Sub-Agent\",\n \"approved\": \"Tool call approved\",\n \"denied\": \"Tool call denied\",\n \"approveFailed\": \"Failed to approve\",\n \"denyFailed\": \"Failed to deny\",\n \"timeoutAutoDenied\": \"Timed out, auto denied\",\n \"acknowledge\": \"Got it\",\n \"approvePattern\": \"Always Allow\",\n \"strictModeHint\": \"Always allow is unavailable in strict mode\",\n \"approveExact\": \"Just Once\",\n \"approvalScope\": \"Approval scope\",\n \"details\": \"Details\"\n },\n \"pluginManager\": {\n \"title\": \"Plugin Manager\",\n \"description\": \"Install, manage, and remove plugins without restarting the service.\",\n \"installed\": \"Installed Plugins\",\n \"noPlugins\": \"No plugins installed yet\",\n \"installBtn\": \"Install Plugin\",\n \"publishBtn\": \"Publish Plugin\",\n \"installTitle\": \"Install Plugin\",\n \"dropPrimary\": \"Drop folder or ZIP here\",\n \"dropSecondary\": \"or click to browse a ZIP file\",\n \"orFromUrl\": \"or from URL\",\n \"installFromUrl\": \"Install from URL\",\n \"urlPlaceholder\": \"https://example.com/plugin.zip\",\n \"zipOnly\": \"Only ZIP files can be dropped as files — drag a folder for directory install\",\n \"dropFailed\": \"Failed to read dropped folder\",\n \"restartHint\": \"Some plugins (e.g. hooks, monkey-patches) may require an app restart to fully take effect or be removed.\",\n \"installing\": \"Installing...\",\n \"installSuccess\": \"Plugin installed successfully\",\n \"installFailed\": \"Plugin installation failed\",\n \"uninstall\": \"Uninstall\",\n \"uninstallConfirm\": \"Uninstall plugin \\\"{{name}}\\\"? This cannot be undone. Some plugins may require an app restart to be fully removed.\",\n \"uninstalling\": \"Uninstalling...\",\n \"uninstallSuccess\": \"Plugin uninstalled successfully\",\n \"uninstallFailed\": \"Plugin uninstallation failed\",\n \"statusLoaded\": \"Running\",\n \"statusUnloaded\": \"Not loaded\",\n \"type\": \"Type\",\n \"version\": \"Version\",\n \"author\": \"Author\",\n \"unknown\": \"Unknown\",\n \"loadFailed\": \"Failed to load plugin list\",\n \"confirmTitle\": \"Confirm Uninstall\",\n \"officialTitle\": \"Official Plugins\",\n \"officialDesc\": \"Install published plugins from the download CDN.\",\n \"catalogRefresh\": \"Refresh\",\n \"catalogEmpty\": \"No official plugins available (CDN catalog may be empty)\",\n \"catalogLoadFailed\": \"Failed to load official plugin catalog\",\n \"catalogInstall\": \"Install\",\n \"catalogReinstall\": \"Reinstall\",\n \"catalogUpgrade\": \"Upgrade available\",\n \"catalogUpgradeBtn\": \"Upgrade\",\n \"catalogInstalled\": \"Installed\",\n \"catalogSha256\": \"SHA256\",\n \"kindBundle\": \"Bundle\",\n \"kindTool\": \"Tool\",\n \"filterByName\": \"Filter by name\",\n \"filterByKind\": \"Filter by kind\",\n \"marketTitle\": \"Plugin Market\",\n \"marketEmpty\": \"No plugins found in market\",\n \"marketLoadFailed\": \"Failed to load plugin market\",\n \"marketUnavailable\": \"Plugin Market is currently under maintenance, please try again later\",\n \"marketSearch\": \"Search plugins...\",\n \"marketAll\": \"All\",\n \"marketCategory\": \"Filter by category\",\n \"marketSearchResult\": \"Found {{count}} plugins matching \\\"{{keyword}}\\\"\",\n \"marketDetails\": \"Details\",\n \"marketDownloads\": \"Downloads\",\n \"marketDeveloper\": \"Developer\",\n \"marketSortLabel\": \"Sort plugins\",\n \"marketSortDownloads\": \"Most downloaded\",\n \"marketSortUpdated\": \"Recently updated\",\n \"marketSortFavorites\": \"Most favorited\",\n \"compatWarningTitle\": \"Compatibility Warning\",\n \"compatWarningContent\": \"This plugin is labeled for QwenPaw {{labels}}. Your QwenPaw version is {{version}}. Installing it may cause errors. Are you sure you want to continue?\",\n \"compatWarningConfirm\": \"Install anyway\"\n },\n \"codingMode\": {\n \"title\": \"Coding Mode\",\n \"experimental\": \"Experimental Feature\",\n \"experimentalDesc\": \"Coding Mode transforms QwenPaw into a lightweight Web IDE with a code editor, file tree, Git integration, and AI-powered coding assistance.\",\n \"experimentalNote\": \"This feature is experimental and still under active development. Some functionality may be limited or change in future updates.\",\n \"confirmBtn\": \"Confirm\",\n \"enterTooltip\": \"Enter Coding Mode\",\n \"exitTooltip\": \"Exit Coding Mode\",\n \"btnCode\": \"Code\",\n \"btnChat\": \"Chat\",\n \"selectProject\": \"Select Coding Project\",\n \"selectProjectDesc\": \"Choose a project to work on, or use the default workspace\",\n \"tabWorkspace\": \"Default Workspace\",\n \"tabClone\": \"Clone Repository\",\n \"tabLocal\": \"Import Local Project\",\n \"tabNew\": \"New Project\",\n \"workspaceDesc\": \"Use the current agent's workspace directory\",\n \"cloneUrl\": \"Git Repository URL\",\n \"cloneUrlPlaceholder\": \"https://github.com/user/repo.git\",\n \"cloneName\": \"Project Name (optional)\",\n \"cloneNamePlaceholder\": \"Leave empty to infer from URL\",\n \"cloneBtn\": \"Start Clone\",\n \"cloning\": \"Cloning...\",\n \"localPath\": \"Local Directory Path\",\n \"localPathPlaceholder\": \"/Users/yourname/my-project\",\n \"localPathHint\": \"Enter the absolute path to your local project\",\n \"openBtn\": \"Import\",\n \"newName\": \"Project Name\",\n \"newNamePlaceholder\": \"my-project\",\n \"createBtn\": \"Create Project\",\n \"switchProject\": \"Switch Project\",\n \"currentProject\": \"Current Project\",\n \"resetToWorkspace\": \"Reset to Default Workspace\",\n \"dropPrimary\": \"Drag a folder here, or click to browse\",\n \"dropSecondary\": \"Drag from Finder or paste a path below\",\n \"pathHint\": \"Please enter the full absolute path (starting with / or ~)\",\n \"importing\": \"Importing...\",\n \"dropFailed\": \"Failed to read folder\",\n \"workingDir\": \"Working directory\",\n \"importCopyNote\": \"The folder will be copied into coding_projects/. Original files will not be modified.\",\n \"importCopyDesc\": \"Import creates a copy of the folder. The Agent works on the copy and your original files stay untouched. To work directly on the original, use \\\"Open Directory\\\" instead.\",\n \"tabOpenDir\": \"Open Directory\",\n \"openDirDesc\": \"Open an existing local directory — no files will be copied\",\n \"openDirBtn\": \"Open This Directory\",\n \"openDirLoading\": \"Loading...\",\n \"openDirEmpty\": \"No subdirectories here\",\n \"openDirHome\": \"Home\",\n \"openDirRefresh\": \"Refresh\",\n \"openDirHiddenFolders\": \"Hidden Folders\"\n },\n \"market\": {\n \"searchPlaceholder\": \"Search skills across platforms\",\n \"categoryAll\": \"All\",\n \"categoryPlaceholder\": \"Search category\",\n \"searchResult\": \"Found {{count}} skills matching \\\"{{keyword}}\\\"\",\n \"providerUnavailable\": \"Provider unavailable\",\n \"noResults\": \"No results\",\n \"startTyping\": \"Type a query above to search the market\",\n \"browseHint\": \"Pick a category or enter a keyword to browse skills in {{providers}}\",\n \"noDescription\": \"No description provided\",\n \"browseMarket\": \"Browse Market\",\n \"installQueue\": \"Install queue\",\n \"clearCompleted\": \"Clear\",\n \"retry\": \"Retry\",\n \"status\": {\n \"queued\": \"Queued\",\n \"installing\": \"Installing\",\n \"completed\": \"Done\",\n \"failed\": \"Failed\",\n \"cancelled\": \"Cancelled\"\n },\n \"loadMore\": \"Load more\",\n \"noMoreResults\": \"No more results\",\n \"detail\": {\n \"title\": \"Skill details\",\n \"version\": \"Version\",\n \"author\": \"Author\",\n \"sourceUrl\": \"Install URL\",\n \"slug\": \"Slug\",\n \"missing\": \"—\"\n },\n \"stats\": {\n \"downloads\": \"Downloads\",\n \"stars\": \"Stars\",\n \"installs\": \"Installs\",\n \"likes\": \"Likes\",\n \"views\": \"Views\",\n \"category\": \"Category\",\n \"updatedAt\": \"Updated\"\n },\n \"queueMsg\": {\n \"timedOut\": \"Install timed out\",\n \"failedPrefix\": \"Failed: {{msg}}\"\n }\n },\n \"startup\": {\n \"starting\": \"Starting backend...\",\n \"checking\": \"Connecting to backend...\",\n \"error\": \"Backend failed to start.\",\n \"errorHint\": \"The backend process could not be launched. Check application logs for details.\",\n \"errorDetails\": \"Show error details\",\n \"timeout\": \"Backend failed to start within {{seconds}} seconds.\",\n \"timeoutHint\": \"Backend failed to start. Please retry, or check application logs for details.\",\n \"retry\": \"Retry\"\n },\n \"tool\": {\n \"readFile\": \"Read {{file}}\",\n \"readFileDefault\": \"Read file\",\n \"writeFile\": \"Create {{file}}\",\n \"writeFileDefault\": \"Create file\",\n \"editFile\": \"Edit {{file}}\",\n \"editFileDefault\": \"Edit file\",\n \"appendFile\": \"Append {{file}}\",\n \"appendFileDefault\": \"Append file\",\n \"grepSearch\": \"Grep {{pattern}}\",\n \"grepSearchDefault\": \"Grep search\",\n \"globSearch\": \"Glob {{pattern}}\",\n \"globSearchDefault\": \"Glob search\",\n \"viewImage\": \"View image {{file}}\",\n \"viewImageDefault\": \"View image\",\n \"viewVideo\": \"View video {{file}}\",\n \"viewVideoDefault\": \"View video\",\n \"desktopScreenshot\": \"Screenshot desktop\",\n \"sendFile\": \"Send {{file}}\",\n \"sendFileDefault\": \"Send file\",\n \"browserUse\": \"Browse {{detail}}\",\n \"browserNavigate\": \"Browse open {{url}}\",\n \"browserNavigateDefault\": \"Browse open page\",\n \"browserClick\": \"Browse click\",\n \"browserType\": \"Browse type\",\n \"browserSnapshot\": \"Browse snapshot\",\n \"browserScroll\": \"Browse scroll\",\n \"getCurrentTime\": \"Get current time\",\n \"setTimezone\": \"Set {{timezone}}\",\n \"getTokenUsage\": \"Query token usage\",\n \"memorySearch\": \"Search memory {{query}}\",\n \"memorySearchDefault\": \"Search memory\",\n \"listAgents\": \"List agents\",\n \"chatWithAgent\": \"Chat {{agent}}\",\n \"chatWithAgentDefault\": \"Chat agent\",\n \"submitToAgent\": \"Submit {{agent}}{{task}}\",\n \"submitToAgentDefault\": \"Submit task\",\n \"checkAgentTask\": \"Check {{agent}} #{{taskId}}\",\n \"checkAgentTaskAgent\": \"Check {{agent}}\",\n \"checkAgentTaskDefault\": \"Check task\",\n \"delegateExternalAgent\": \"Delegate {{runner}}\",\n \"delegateExternalAgentDefault\": \"Delegate external agent\",\n \"materializeSkill\": \"Create {{skill}}\",\n \"materializeSkillDefault\": \"Create skill\",\n \"shell\": \"Run {{command}}\",\n \"shellDefault\": \"Run command\",\n \"runToolBatch\": \"Run workflow {{workflow}}\",\n \"runToolBatchRunning\": \"Running saved workflow\",\n \"runToolBatchProgress\": \"{{count}} steps\",\n \"runToolBatchStepCount\": \"Total steps: {{count}}\",\n \"formatTable\": {\n \"file\": \"File\",\n \"lineNumber\": \"Line\",\n \"score\": \"Score\",\n \"summary\": \"Summary\",\n \"name\": \"Name\",\n \"id\": \"ID\",\n \"description\": \"Description\",\n \"status\": \"Status\"\n },\n \"loading\": \"Loading...\",\n \"inputProgress\": \"{{count}} chars\",\n \"rawInputPreview\": \"Live raw input (last 4,096 characters)\",\n \"largeOutputOmitted\": \"{{count}} lines omitted — copy the available output or follow its recovery instructions\",\n \"largeOutputTruncated\": \"Output truncated for display — copy the available output or follow its recovery instructions\",\n \"browserAction\": {\n \"startHeaded\": \"Start browser (headed)\",\n \"start\": \"Start browser\",\n \"stop\": \"Stop browser\",\n \"open\": \"Open {{url}}\",\n \"openDefault\": \"Open page\",\n \"navigate\": \"Navigate {{url}}\",\n \"navigateDefault\": \"Navigate\",\n \"navigateBack\": \"Go back\",\n \"click\": \"Click {{selector}}\",\n \"clickDefault\": \"Click element\",\n \"type\": \"Type {{text}}\",\n \"typeDefault\": \"Type text\",\n \"snapshot\": \"Get snapshot\",\n \"screenshot\": \"Screenshot {{path}}\",\n \"screenshotDefault\": \"Screenshot\",\n \"eval\": \"Eval {{code}}\",\n \"evalDefault\": \"Evaluate code\",\n \"runCode\": \"Run {{code}}\",\n \"runCodeDefault\": \"Run code\",\n \"closePage\": \"Close page\",\n \"tabs\": \"Tabs {{action}}\",\n \"tabsDefault\": \"Manage tabs\",\n \"fillForm\": \"Fill form\",\n \"fileUpload\": \"Upload {{filename}}\",\n \"fileUploadDefault\": \"Upload file\",\n \"fileDownload\": \"Download {{target}}\",\n \"fileDownloadDefault\": \"Download file\",\n \"pressKey\": \"Press {{key}}\",\n \"pressKeyDefault\": \"Press key\",\n \"hover\": \"Hover {{selector}}\",\n \"hoverDefault\": \"Hover element\",\n \"drag\": \"Drag\",\n \"selectOption\": \"Select option\",\n \"waitFor\": \"Wait for {{target}}\",\n \"waitForDefault\": \"Wait for element\",\n \"resize\": \"Resize {{w}}x{{h}}\",\n \"resizeDefault\": \"Resize window\",\n \"pdf\": \"Export PDF {{path}}\",\n \"pdfDefault\": \"Export PDF\",\n \"install\": \"Install\",\n \"batch\": \"Batch\"\n },\n \"lineBadge\": {\n \"lines\": \"{{count}} lines\",\n \"addLines\": \"+{{count}} lines\",\n \"delLines\": \"-{{count}} lines\",\n \"matches\": \"{{count}} matches\",\n \"files\": \"{{count}} files\"\n },\n \"inlineResult\": {\n \"taskId\": \"Task #{{id}}\",\n \"submitted\": \"Submitted\"\n },\n \"execute\": \"Run {{tool}}\",\n \"executeDefault\": \"Run tool\",\n \"control\": {\n \"title\": \"Tool running — offload to background?\",\n \"offload\": \"Move to background\",\n \"keep\": \"Keep Foreground\",\n \"extendOffload\": \"Delay Offload\",\n \"extendKill\": \"Extend timeout\",\n \"cancel\": \"Cancel\",\n \"autoAction\": \"{{seconds}}s until default action: \",\n \"noCountdown\": \"No countdown · Default: \",\n \"policyOffload\": \"Offload\",\n \"policyKeep\": \"Keep Foreground\",\n \"manage\": \"Manage\",\n \"offloadMode\": {\n \"title\": \"Tool running longer — about to offload\",\n \"offloadNow\": \"Move to background now\",\n \"preventOffload\": \"Don't auto-offload\",\n \"delayOffload\": \"Delay offload\",\n \"countdownCaption\": \"until auto-offload\",\n \"footerNoCountdown\": \"No countdown · Default: offload\",\n \"ringAria\": \"{{seconds}}s until auto-offload\",\n \"toastAuto\": \"Moved to background automatically\",\n \"preventedTitle\": \"Auto-offload disabled\",\n \"preventedFooter\": \"Won't auto-offload; you can move to background or cancel\"\n },\n \"keepMode\": {\n \"title\": \"Tool still running in foreground\",\n \"offload\": \"Move to background\",\n \"countdownCaption\": \"until panel closes\",\n \"footerNoCountdown\": \"Stays in foreground by default; you can move to background anytime\",\n \"ringAria\": \"Panel closes in {{seconds}}s\",\n \"toastDismiss\": \"Reminder closed; tool keeps running in foreground\"\n },\n \"toast\": {\n \"offloaded\": \"Tool moved to background\",\n \"keepWaiting\": \"Continuing to wait…\",\n \"extended\": \"Offload delayed; will remind you in +30s\",\n \"killExtended\": \"Timeout extended by 30s\",\n \"cancelled\": \"Tool call cancelled\",\n \"cancelFailed\": \"Failed to cancel tool\",\n \"actionFailed\": \"Action failed. The tool may have finished or the request was rejected.\",\n \"notFound\": \"Tool call not found (it may have already finished).\",\n \"conflict\": \"Action rejected (tool state changed or limit reached).\",\n \"networkError\": \"Network error. Check your connection and try again.\",\n \"bgComplete\": \"Background tool complete: {{tool}}\",\n \"bgCancelled\": \"Background tool cancelled: {{tool}}\"\n },\n \"hint\": {\n \"completed\": \"Tool {{tool}} completed in background\",\n \"cancelled\": \"Tool {{tool}} cancelled\"\n },\n \"bgQueue\": {\n \"title\": \"Background tasks\",\n \"running\": \"Running\",\n \"done\": \"Done\",\n \"cancelled\": \"Cancelled\",\n \"totalDuration\": \"Total\",\n \"cancel\": \"Cancel\",\n \"remove\": \"Remove\",\n \"cancelAll\": \"Cancel all\",\n \"clearAll\": \"Clear all\",\n \"cancelAllDone\": \"Cancelled all running tasks\",\n \"clearAllDone\": \"Cleared background task list\",\n \"noOutput\": \"No output yet\"\n }\n }\n },\n \"preview\": {\n \"error\": {\n \"SENSITIVE_FILE_BLOCKED\": \"This file is blocked by File Guard (sensitive file protection).\",\n \"OUTSIDE_WORKSPACE\": \"This file is outside the workspace scope. Enable \\\"Allow Console Preview Outside Workspace\\\" in Settings → Security → File Guard.\",\n \"FORBIDDEN\": \"Access denied: insufficient permissions to preview this file.\",\n \"NOT_FOUND\": \"File not found. It may have been moved or deleted. If a relative path was used, please try using an absolute path instead.\",\n \"LOAD_FAILED\": \"Failed to load file preview.\",\n \"LOAD_FAILED_DETAIL\": \"Failed to load file preview: {{detail}}\"\n }\n },\n \"loop\": {\n \"budget\": {\n \"low\": \"Low\",\n \"medium\": \"Med\",\n \"high\": \"High\"\n },\n \"status\": {\n \"pause\": \"Pause\",\n \"resume\": \"Resume\",\n \"stop\": \"Stop\"\n },\n \"gotoSettings\": \"Agent Loop Settings\",\n \"selectorTitle\": \"Loop mode\",\n \"selectorHint\": \"Choose how the agent should continue\",\n \"selectorAria\": \"Select loop mode\",\n \"builtInModes\": \"Built-in\",\n \"customModes\": \"Custom and plugins\",\n \"starting\": \"Starting\",\n \"running\": \"Running\",\n \"awaiting_user\": \"Waiting for your reply\",\n \"activePersistentDescription\": \"This mode remains active for the conversation until it finishes or you start over.\",\n \"activeCustomDescription\": \"This custom mode remains active until you use /mode off, /clear, or /new.\",\n \"loadError\": \"Some loop modes could not be loaded.\",\n \"retry\": \"Retry\",\n \"modes\": {\n \"default\": {\n \"name\": \"Default\",\n \"description\": \"The standard guarded agent loop.\"\n },\n \"goal\": {\n \"name\": \"Goal\",\n \"description\": \"Work persistently toward a concrete, verifiable goal.\"\n },\n \"mission\": {\n \"name\": \"Mission\",\n \"description\": \"Run a structured, persistent multi-step task.\"\n }\n }\n }\n}\n"}}, "plugins/apps/qwenpaw-creator/plugin.json": {"db8fb6391097791b08f63623ba4cd88421ec5cdb": {"t": 1785846860.8917994, "n": 13016, "h": "6d5dc3c9e7e1da73", "c": "{\n \"id\": \"qwenpaw-creator\",\n \"name\": \"QwenPaw Creator\",\n \"version\": \"1.0.1\",\n \"type\": \"app\",\n \"description\": \"QwenPaw plugin that brings the QwenPaw-Creator script-to-assets-to-video workflow into QwenPaw.\",\n \"description_i18n\": {\n \"zh-CN\": \"将 QwenPaw-Creator 的剧本、资产、分镜与视频工作流作为插件接入 QwenPaw。\",\n \"en-US\": \"QwenPaw plugin that brings the QwenPaw-Creator script, asset, storyboard, and video workflow into QwenPaw.\"\n },\n \"author\": \"QwenPaw Creator Team\",\n \"entry\": {\n \"backend\": \"backend/main.py\",\n \"frontend\": \"ui/dist/index.js\"\n },\n \"dependencies\": [\n \"oss2\",\n \"imageio-ffmpeg\"\n ],\n \"qwenpaw_version\": {\n \"min\": \"2.0.1\",\n \"max\": \"3.1.0\"\n },\n \"meta\": {\n \"pawapp\": {\n \"icon\": \"🎬\",\n \"icon_url\": \"/api/frontend_plugin/qwenpaw-creator/files/ui/dist/app/logo-mark.png\",\n \"entry_page\": \"/apps/qwenpaw-creator\",\n \"launch_scope\": \"page\",\n \"category\": \"video-creation\"\n },\n \"permissions\": {\n \"chat\": false,\n \"storage\": false\n },\n \"runtime_dependencies\": {\n \"jq\": {\n \"required_for\": \"jq_project agent edits\",\n \"path_env\": \"CREATOR_JQ_PATH\",\n \"auto_install_env\": \"CREATOR_AUTO_INSTALL_BINARIES\",\n \"auto_install_default\": false\n },\n \"ffmpeg\": {\n \"required_for\": \"media probing, rendering, and composition\",\n \"path_env\": \"CREATOR_FFMPEG_PATH\",\n \"bundled_fallback\": \"imageio-ffmpeg\"\n },\n \"ffprobe\": {\n \"required\": false,\n \"path_env\": \"CREATOR_FFPROBE_PATH\",\n \"fallback\": \"ffmpeg metadata probing\"\n }\n },\n \"features\": [\n \"project-management\",\n \"script-generation\",\n \"asset-generation\",\n \"storyboard-editing\",\n \"reference-to-video\",\n \"video-stitching\"\n ],\n \"tools\": [\n {\n \"name\": \"creator_text_model\",\n \"description\": \"QwenPaw Creator text model configuration for script, storyboard, and prompt planning\",\n \"icon\": \"📝\",\n \"requires_config\": true,\n \"config_fields\": [\n {\n \"name\": \"api_key\",\n \"label\": \"Text API Key\",\n \"type\": \"password\",\n \"required\": true,\n \"placeholder\": \"sk-...\",\n \"help\": \"Used only by Creator text workflows. Environment fallback: TEXT_API_KEY.\"\n },\n {\n \"name\": \"model\",\n \"label\": \"Text Model\",\n \"type\": \"text\",\n \"required\": false,\n \"default\": \"qwen3.7-plus\",\n \"placeholder\": \"qwen3.7-plus\"\n },\n {\n \"name\": \"base_url\",\n \"label\": \"Text Base URL\",\n \"type\": \"text\",\n \"required\": false,\n \"placeholder\": \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n }\n ]\n },\n {\n \"name\": \"creator_vlm_model\",\n \"description\": \"QwenPaw Creator native multimodal understanding configuration\",\n \"icon\": \"👁️\",\n \"requires_config\": false,\n \"config_fields\": [\n {\n \"name\": \"api_key\",\n \"label\": \"VLM API Key\",\n \"type\": \"password\",\n \"required\": false,\n \"placeholder\": \"Uses Text API Key when empty\",\n \"help\": \"Optional. Falls back to creator_text_model / TEXT_API_KEY.\"\n },\n {\n \"name\": \"model\",\n \"label\": \"VLM Model\",\n \"type\": \"text\",\n \"required\": false,\n \"default\": \"qwen3.7-plus\",\n \"placeholder\": \"qwen3.7-plus\"\n },\n {\n \"name\": \"base_url\",\n \"label\": \"VLM Base URL\",\n \"type\": \"text\",\n \"required\": false,\n \"placeholder\": \"Uses Text Base URL when empty\"\n }\n ]\n },\n {\n \"name\": \"creator_web_grounding\",\n \"description\": \"QwenPaw Creator web and image grounding configuration\",\n \"icon\": \"🌐\",\n \"requires_config\": false,\n \"config_fields\": [\n {\n \"name\": \"enabled\",\n \"label\": \"Grounding Enabled\",\n \"type\": \"select\",\n \"required\": false,\n \"default\": \"true\",\n \"options\": [\"true\", \"false\"]\n },\n {\n \"name\": \"tavily_api_key\",\n \"label\": \"Tavily API Key\",\n \"type\": \"password\",\n \"required\": false,\n \"placeholder\": \"tvly-...\",\n \"help\": \"Optional primary text and image retrieval provider.\"\n },\n {\n \"name\": \"native_search_enabled\",\n \"label\": \"Qwen Native Search Fallback\",\n \"type\": \"select\",\n \"required\": false,\n \"default\": \"true\",\n \"options\": [\"true\", \"false\"],\n \"help\": \"Uses DashScope Responses web_search/web_search_image only when the selected search adapter is compatible.\"\n },\n {\n \"name\": \"search_reuse_llm\",\n \"label\": \"Reuse Creator LLM for Search\",\n \"type\": \"select\",\n \"required\": false,\n \"default\": \"true\",\n \"options\": [\"true\", \"false\"]\n },\n {\n \"name\": \"search_api_key\",\n \"label\": \"Qwen Search API Key\",\n \"type\": \"password\",\n \"required\": false,\n \"help\": \"Used only when search_reuse_llm is false.\"\n },\n {\n \"name\": \"search_model\",\n \"label\": \"Qwen Search Model\",\n \"type\": \"text\",\n \"required\": false,\n \"help\": \"Used only when search_reuse_llm is false.\"\n },\n {\n \"name\": \"search_base_url\",\n \"label\": \"Qwen Search Base URL\",\n \"type\": \"text\",\n \"required\": false,\n \"help\": \"Used only when search_reuse_llm is false.\"\n },\n {\n \"name\": \"validation_source\",\n \"label\": \"Validation Model Source\",\n \"type\": \"select\",\n \"required\": false,\n \"default\": \"llm\",\n \"options\": [\"llm\", \"vlm\", \"custom\"],\n \"help\": \"Which model verifies grounded images: the Creator LLM, the Creator VLM, or the custom model below. Overrides the legacy reuse_llm switch.\"\n },\n {\n \"name\": \"reuse_llm\",\n \"label\": \"Reuse Creator LLM for Validation\",\n \"type\": \"select\",\n \"required\": false,\n \"default\": \"true\",\n \"options\": [\"true\", \"false\"],\n \"help\": \"Legacy switch; ignored when validation_source is set.\"\n },\n {\n \"name\": \"api_key\",\n \"label\": \"Grounding LLM API Key\",\n \"type\": \"password\",\n \"required\": false,\n \"help\": \"Used only when reuse_llm is false. Must support image input for visual verification.\"\n },\n {\n \"name\": \"model\",\n \"label\": \"Grounding LLM Model\",\n \"type\": \"text\",\n \"required\": false,\n \"help\": \"Used only when reuse_llm is false.\"\n },\n {\n \"name\": \"base_url\",\n \"label\": \"Grounding LLM Base URL\",\n \"type\": \"text\",\n \"required\": false,\n \"help\": \"Used only when reuse_llm is false.\"\n }\n ]\n },\n {\n \"name\": \"creator_image_model\",\n \"description\": \"QwenPaw Creator image generation/editing configuration\",\n \"icon\": \"🎨\",\n \"requires_config\": true,\n \"config_fields\": [\n {\n \"name\": \"api_key\",\n \"label\": \"Image API Key\",\n \"type\": \"password\",\n \"required\": true,\n \"placeholder\": \"sk-...\",\n \"help\": \"Used only by Creator image workflows. Environment fallback: IMAGE_API_KEY.\"\n },\n {\n \"name\": \"base_url\",\n \"label\": \"Image Base URL\",\n \"type\": \"text\",\n \"required\": false,\n \"default\": \"https://routify.alibaba-inc.com/protocol/openai\",\n \"placeholder\": \"https://routify.alibaba-inc.com/protocol/openai\"\n },\n {\n \"name\": \"model\",\n \"label\": \"Image Model\",\n \"type\": \"text\",\n \"required\": false,\n \"default\": \"gpt-image-2\",\n \"placeholder\": \"gpt-image-2\"\n },\n {\n \"name\": \"quality\",\n \"label\": \"Image Quality\",\n \"type\": \"select\",\n \"required\": false,\n \"default\": \"low\",\n \"options\": [\"low\", \"medium\", \"high\", \"auto\"]\n },\n {\n \"name\": \"timeout\",\n \"label\": \"Image Timeout (seconds)\",\n \"type\": \"number\",\n \"required\": false,\n \"default\": 120,\n \"min\": 30,\n \"max\": 600\n }\n ]\n },\n {\n \"name\": \"creator_asr_model\",\n \"description\": \"QwenPaw Creator speech-to-text configuration for source understanding\",\n \"icon\": \"🎙️\",\n \"requires_config\": false,\n \"config_fields\": [\n {\"name\": \"provider\", \"label\": \"ASR Provider\", \"type\": \"select\", \"required\": true, \"default\": \"fun-asr\", \"options\": [\"fun-asr\", \"whisper\"]},\n {\"name\": \"api_key\", \"label\": \"ASR API Key\", \"type\": \"password\", \"required\": false, \"help\": \"Fun-ASR may reuse creator_text_model when enabled in Creator settings.\"},\n {\"name\": \"base_url\", \"label\": \"ASR Base URL\", \"type\": \"text\", \"required\": true, \"default\": \"https://dashscope.aliyuncs.com/api/v1\"},\n {\"name\": \"model\", \"label\": \"ASR Model\", \"type\": \"text\", \"required\": true, \"default\": \"fun-asr\"},\n {\"name\": \"language\", \"label\": \"Language\", \"type\": \"text\", \"required\": false}\n ]\n },\n {\n \"name\": \"creator_video_model\",\n \"description\": \"QwenPaw Creator reference-to-video configuration\",\n \"icon\": \"🎬\",\n \"requires_config\": true,\n \"config_fields\": [\n {\n \"name\": \"api_key\",\n \"label\": \"Video API Key\",\n \"type\": \"password\",\n \"required\": true,\n \"placeholder\": \"sk-...\",\n \"help\": \"Used only by Creator video workflows. Environment fallback: VIDEO_API_KEY.\"\n },\n {\n \"name\": \"base_url\",\n \"label\": \"Video Base URL\",\n \"type\": \"text\",\n \"required\": false,\n \"default\": \"https://dashscope.aliyuncs.com/api/v1\",\n \"placeholder\": \"https://dashscope.aliyuncs.com/api/v1\"\n },\n {\n \"name\": \"model\",\n \"label\": \"Video Model\",\n \"type\": \"text\",\n \"required\": false,\n \"default\": \"wan2.7-r2v\",\n \"placeholder\": \"wan2.7-r2v\",\n \"help\": \"Supported: Bailian wan2.x r2v family, Bailian happyhorse-1.x-r2v, Volcano doubao-seedance-2.x. Environment fallback: VIDEO_MODEL_NAME.\"\n },\n {\n \"name\": \"submit_timeout\",\n \"label\": \"Submit Timeout (seconds)\",\n \"type\": \"number\",\n \"required\": false,\n \"default\": 180,\n \"min\": 30,\n \"max\": 1800\n },\n {\n \"name\": \"status_timeout\",\n \"label\": \"Status Timeout (seconds)\",\n \"type\": \"number\",\n \"required\": false,\n \"default\": 30,\n \"min\": 10,\n \"max\": 300\n }\n ]\n },\n {\n \"name\": \"creator_media_oss\",\n \"description\": \"QwenPaw Creator OSS and provider-bound media upload configuration\",\n \"icon\": \"🪣\",\n \"requires_config\": false,\n \"config_fields\": [\n {\n \"name\": \"policy_api_key\",\n \"label\": \"DashScope Upload Policy API Key\",\n \"type\": \"password\",\n \"required\": false,\n \"help\": \"Used for DashScope model-bound temporary uploads. Environment fallback: OSS_POLICY_API_KEY.\"\n },\n {\n \"name\": \"access_key_id\",\n \"label\": \"OSS Access Key ID\",\n \"type\": \"text\",\n \"required\": false,\n \"help\": \"Environment fallback: OSS_ACCESS_KEY_ID.\"\n },\n {\n \"name\": \"access_key_secret\",\n \"label\": \"OSS Access Key Secret\",\n \"type\": \"password\",\n \"required\": false,\n \"help\": \"Environment fallback: OSS_ACCESS_KEY_SECRET.\"\n },\n {\n \"name\": \"endpoint\",\n \"label\": \"OSS Endpoint\",\n \"type\": \"text\",\n \"required\": false,\n \"placeholder\": \"https://oss-cn-hangzhou.aliyuncs.com\"\n },\n {\n \"name\": \"bucket\",\n \"label\": \"OSS Bucket\",\n \"type\": \"text\",\n \"required\": false,\n \"default\": \"creator-store\"\n },\n {\n \"name\": \"public_base_url\",\n \"label\": \"OSS Public Base URL\",\n \"type\": \"text\",\n \"required\": false,\n \"placeholder\": \"https://cdn.example.com\"\n }\n ]\n }\n ]\n }\n}\n"}}}} |