Files

1 line
374 KiB
JSON

{"repo": "bhaveshpabnani/humungousaur", "files": {"humungousaur/planning/model_providers.py": {"15d8b78fa52cafc8c0192e523f8a5eba43c7963b": {"t": 1785595160.2659104, "n": 15414, "h": "feec3bb7009cb605", "c": "from __future__ import annotations\n\nfrom dataclasses import dataclass\nimport os\n\n\n@dataclass(frozen=True, slots=True)\nclass ModelProviderSpec:\n provider_id: str\n label: str\n transport: str\n default_model: str\n model_env: str\n api_key_envs: tuple[str, ...]\n default_base_url: str\n base_url_env: str = \"\"\n aliases: tuple[str, ...] = ()\n allow_openai_fallback: bool = True\n\n @property\n def primary_api_key_env(self) -> str:\n return self.api_key_envs[0] if self.api_key_envs else \"\"\n\n\nOPENAI_RESPONSES = \"openai_responses\"\nOPENAI_CHAT = \"openai_chat\"\nANTHROPIC_MESSAGES = \"anthropic_messages\"\nEXTERNAL_RUNTIME = \"external_runtime\"\n\n\nMODEL_PROVIDER_REGISTRY: tuple[ModelProviderSpec, ...] = (\n ModelProviderSpec(\n \"openai-responses\",\n \"OpenAI Responses\",\n OPENAI_RESPONSES,\n \"gpt-5-mini\",\n \"OPENAI_MODEL\",\n (\"OPENAI_API_KEY\",),\n \"https://api.openai.com/v1\",\n \"OPENAI_BASE_URL\",\n aliases=(\"openai\", \"openai-api\"),\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"openai-chat\",\n \"OpenAI Chat\",\n OPENAI_CHAT,\n \"gpt-5-mini\",\n \"OPENAI_MODEL\",\n (\"OPENAI_API_KEY\",),\n \"https://api.openai.com/v1\",\n \"OPENAI_BASE_URL\",\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"openrouter\",\n \"OpenRouter\",\n OPENAI_CHAT,\n \"anthropic/claude-sonnet-4.6\",\n \"OPENROUTER_MODEL\",\n (\"OPENROUTER_API_KEY\",),\n \"https://openrouter.ai/api/v1\",\n \"OPENROUTER_BASE_URL\",\n ),\n ModelProviderSpec(\n \"nous\",\n \"Nous Portal\",\n OPENAI_CHAT,\n \"moonshotai/kimi-k2.6\",\n \"NOUS_MODEL\",\n (\"NOUS_API_KEY\", \"NOUS_INFERENCE_API_KEY\"),\n \"https://inference-api.nousresearch.com/v1\",\n \"NOUS_INFERENCE_BASE_URL\",\n ),\n ModelProviderSpec(\n \"novita\",\n \"NovitaAI\",\n OPENAI_CHAT,\n \"deepseek/deepseek-r1\",\n \"NOVITA_MODEL\",\n (\"NOVITA_API_KEY\",),\n \"https://api.novita.ai/openai/v1\",\n \"NOVITA_BASE_URL\",\n ),\n ModelProviderSpec(\n \"lmstudio\",\n \"LM Studio\",\n OPENAI_CHAT,\n \"local-model\",\n \"LM_MODEL\",\n (\"LM_API_KEY\",),\n \"http://127.0.0.1:1234/v1\",\n \"LM_BASE_URL\",\n ),\n ModelProviderSpec(\n \"anthropic\",\n \"Anthropic\",\n ANTHROPIC_MESSAGES,\n \"claude-sonnet-4-6\",\n \"ANTHROPIC_MODEL\",\n (\"ANTHROPIC_API_KEY\", \"ANTHROPIC_TOKEN\", \"CLAUDE_CODE_OAUTH_TOKEN\"),\n \"https://api.anthropic.com\",\n \"ANTHROPIC_BASE_URL\",\n ),\n ModelProviderSpec(\n \"alibaba\",\n \"Qwen Cloud\",\n OPENAI_CHAT,\n \"qwen-max\",\n \"DASHSCOPE_MODEL\",\n (\"DASHSCOPE_API_KEY\",),\n \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1\",\n \"DASHSCOPE_BASE_URL\",\n aliases=(\"qwen\", \"dashscope\"),\n ),\n ModelProviderSpec(\n \"xai\",\n \"xAI Grok\",\n OPENAI_CHAT,\n \"grok-4.3\",\n \"XAI_MODEL\",\n (\"XAI_API_KEY\",),\n \"https://api.x.ai/v1\",\n \"XAI_BASE_URL\",\n aliases=(\"grok\", \"x-ai\"),\n ),\n ModelProviderSpec(\n \"gemini\",\n \"Google Gemini\",\n OPENAI_CHAT,\n \"gemini-3-pro-preview\",\n \"GEMINI_MODEL\",\n (\"GOOGLE_API_KEY\", \"GEMINI_API_KEY\"),\n \"https://generativelanguage.googleapis.com/v1beta/openai\",\n \"GEMINI_BASE_URL\",\n aliases=(\"google\", \"google-ai-studio\"),\n ),\n ModelProviderSpec(\n \"deepseek\",\n \"DeepSeek\",\n OPENAI_CHAT,\n \"deepseek-chat\",\n \"DEEPSEEK_MODEL\",\n (\"DEEPSEEK_API_KEY\",),\n \"https://api.deepseek.com/v1\",\n \"DEEPSEEK_BASE_URL\",\n ),\n ModelProviderSpec(\n \"mistral\",\n \"Mistral\",\n OPENAI_CHAT,\n \"mistral-large-latest\",\n \"MISTRAL_MODEL\",\n (\"MISTRAL_API_KEY\",),\n \"https://api.mistral.ai/v1\",\n \"MISTRAL_BASE_URL\",\n ),\n ModelProviderSpec(\n \"groq\",\n \"Groq\",\n OPENAI_CHAT,\n \"llama-3.3-70b-versatile\",\n \"GROQ_MODEL\",\n (\"GROQ_API_KEY\",),\n \"https://api.groq.com/openai/v1\",\n \"GROQ_BASE_URL\",\n ),\n ModelProviderSpec(\n \"cerebras\",\n \"Cerebras\",\n OPENAI_CHAT,\n \"llama3.3-70b\",\n \"CEREBRAS_MODEL\",\n (\"CEREBRAS_API_KEY\",),\n \"https://api.cerebras.ai/v1\",\n \"CEREBRAS_BASE_URL\",\n ),\n ModelProviderSpec(\n \"ollama\",\n \"Ollama\",\n OPENAI_CHAT,\n \"llama3.1\",\n \"OLLAMA_MODEL\",\n (\"OLLAMA_API_KEY\",),\n \"http://127.0.0.1:11434/v1\",\n \"OLLAMA_BASE_URL\",\n ),\n ModelProviderSpec(\n \"ollama-cloud\",\n \"Ollama Cloud\",\n OPENAI_CHAT,\n \"gpt-oss:20b\",\n \"OLLAMA_CLOUD_MODEL\",\n (\"OLLAMA_API_KEY\",),\n \"https://ollama.com/v1\",\n \"OLLAMA_CLOUD_BASE_URL\",\n ),\n ModelProviderSpec(\n \"local-openai\",\n \"Local OpenAI-compatible\",\n OPENAI_CHAT,\n \"llama3.1\",\n \"LOCAL_LLM_MODEL\",\n (\"LOCAL_LLM_API_KEY\",),\n \"http://127.0.0.1:11434/v1\",\n \"LOCAL_LLM_BASE_URL\",\n ),\n ModelProviderSpec(\n \"vercel\",\n \"Vercel AI Gateway\",\n OPENAI_CHAT,\n \"anthropic/claude-sonnet-4.6\",\n \"AI_GATEWAY_MODEL\",\n (\"AI_GATEWAY_API_KEY\", \"VERCEL_OIDC_TOKEN\"),\n \"https://ai-gateway.vercel.sh/v1\",\n \"AI_GATEWAY_BASE_URL\",\n aliases=(\"ai-gateway\", \"vercel-ai-gateway\"),\n ),\n ModelProviderSpec(\n \"litellm\",\n \"LiteLLM\",\n OPENAI_CHAT,\n \"openai/gpt-5-mini\",\n \"LITELLM_MODEL\",\n (\"LITELLM_API_KEY\",),\n \"http://localhost:4000/v1\",\n \"LITELLM_BASE_URL\",\n ),\n ModelProviderSpec(\n \"nvidia\",\n \"NVIDIA NIM\",\n OPENAI_CHAT,\n \"nvidia/nemotron-3-super-120b-a12b\",\n \"NVIDIA_MODEL\",\n (\"NVIDIA_API_KEY\",),\n \"https://integrate.api.nvidia.com/v1\",\n \"NVIDIA_BASE_URL\",\n aliases=(\"nim\",),\n ),\n ModelProviderSpec(\n \"huggingface\",\n \"Hugging Face\",\n OPENAI_CHAT,\n \"meta-llama/Llama-3.3-70B-Instruct\",\n \"HF_MODEL\",\n (\"HF_TOKEN\", \"HUGGINGFACE_API_KEY\"),\n \"https://router.huggingface.co/v1\",\n \"HF_BASE_URL\",\n aliases=(\"hf\",),\n ),\n ModelProviderSpec(\n \"zai\",\n \"Z.AI / GLM\",\n OPENAI_CHAT,\n \"glm-5.1\",\n \"GLM_MODEL\",\n (\"GLM_API_KEY\", \"ZAI_API_KEY\", \"Z_AI_API_KEY\"),\n \"https://api.z.ai/api/paas/v4\",\n \"GLM_BASE_URL\",\n aliases=(\"z-ai\", \"glm\", \"zhipu\"),\n ),\n ModelProviderSpec(\n \"kimi-coding\",\n \"Kimi / Moonshot\",\n OPENAI_CHAT,\n \"kimi-k2.6\",\n \"KIMI_MODEL\",\n (\"KIMI_API_KEY\", \"KIMI_CODING_API_KEY\"),\n \"https://api.moonshot.ai/v1\",\n \"KIMI_BASE_URL\",\n aliases=(\"moonshot\", \"kimi\"),\n ),\n ModelProviderSpec(\n \"kimi-coding-cn\",\n \"Kimi / Moonshot China\",\n OPENAI_CHAT,\n \"kimi-k2.6\",\n \"KIMI_CN_MODEL\",\n (\"KIMI_CN_API_KEY\",),\n \"https://api.moonshot.cn/v1\",\n \"KIMI_CN_BASE_URL\",\n ),\n ModelProviderSpec(\n \"stepfun\",\n \"StepFun Step Plan\",\n OPENAI_CHAT,\n \"step-3.7-flash\",\n \"STEPFUN_MODEL\",\n (\"STEPFUN_API_KEY\",),\n \"https://api.stepfun.ai/step_plan/v1\",\n \"STEPFUN_BASE_URL\",\n ),\n ModelProviderSpec(\n \"minimax\",\n \"MiniMax\",\n ANTHROPIC_MESSAGES,\n \"minimax-m3\",\n \"MINIMAX_MODEL\",\n (\"MINIMAX_API_KEY\",),\n \"https://api.minimax.io/anthropic\",\n \"MINIMAX_BASE_URL\",\n ),\n ModelProviderSpec(\n \"minimax-cn\",\n \"MiniMax China\",\n ANTHROPIC_MESSAGES,\n \"minimax-m3\",\n \"MINIMAX_CN_MODEL\",\n (\"MINIMAX_CN_API_KEY\",),\n \"https://api.minimaxi.com/anthropic\",\n \"MINIMAX_CN_BASE_URL\",\n ),\n ModelProviderSpec(\n \"arcee\",\n \"Arcee AI\",\n OPENAI_CHAT,\n \"auto\",\n \"ARCEE_MODEL\",\n (\"ARCEEAI_API_KEY\", \"ARCEE_API_KEY\"),\n \"https://api.arcee.ai/api/v1\",\n \"ARCEE_BASE_URL\",\n ),\n ModelProviderSpec(\n \"gmi\",\n \"GMI Cloud\",\n OPENAI_CHAT,\n \"auto\",\n \"GMI_MODEL\",\n (\"GMI_API_KEY\",),\n \"https://api.gmi-serving.com/v1\",\n \"GMI_BASE_URL\",\n ),\n ModelProviderSpec(\n \"xiaomi\",\n \"Xiaomi MiMo\",\n OPENAI_CHAT,\n \"mimo-v2.5-pro\",\n \"XIAOMI_MODEL\",\n (\"XIAOMI_API_KEY\",),\n \"https://api.xiaomimimo.com/v1\",\n \"XIAOMI_BASE_URL\",\n ),\n ModelProviderSpec(\n \"tencent-tokenhub\",\n \"Tencent TokenHub\",\n OPENAI_CHAT,\n \"tencent/hy3-preview\",\n \"TOKENHUB_MODEL\",\n (\"TOKENHUB_API_KEY\",),\n \"https://tokenhub.tencentmaas.com/v1\",\n \"TOKENHUB_BASE_URL\",\n aliases=(\"tokenhub\", \"tencent\"),\n ),\n ModelProviderSpec(\n \"opencode-zen\",\n \"OpenCode Zen\",\n OPENAI_CHAT,\n \"anthropic/claude-sonnet-4.6\",\n \"OPENCODE_ZEN_MODEL\",\n (\"OPENCODE_ZEN_API_KEY\",),\n \"https://opencode.ai/zen/v1\",\n \"OPENCODE_ZEN_BASE_URL\",\n ),\n ModelProviderSpec(\n \"opencode-go\",\n \"OpenCode Go\",\n OPENAI_CHAT,\n \"z-ai/glm-5.1\",\n \"OPENCODE_GO_MODEL\",\n (\"OPENCODE_GO_API_KEY\",),\n \"https://opencode.ai/zen/go/v1\",\n \"OPENCODE_GO_BASE_URL\",\n ),\n ModelProviderSpec(\n \"kilocode\",\n \"Kilo Code\",\n OPENAI_CHAT,\n \"anthropic/claude-sonnet-4.6\",\n \"KILOCODE_MODEL\",\n (\"KILOCODE_API_KEY\",),\n \"https://api.kilo.ai/api/gateway\",\n \"KILOCODE_BASE_URL\",\n ),\n ModelProviderSpec(\n \"azure-openai\",\n \"Azure OpenAI\",\n OPENAI_CHAT,\n \"gpt-5\",\n \"AZURE_OPENAI_MODEL\",\n (\"AZURE_OPENAI_API_KEY\", \"AZURE_OPENAI_KEY\"),\n \"\",\n \"AZURE_OPENAI_ENDPOINT\",\n aliases=(\"azure\",),\n ),\n ModelProviderSpec(\n \"azure-foundry\",\n \"Azure Foundry\",\n OPENAI_CHAT,\n \"gpt-5\",\n \"AZURE_FOUNDRY_MODEL\",\n (\"AZURE_FOUNDRY_API_KEY\",),\n \"\",\n \"AZURE_FOUNDRY_BASE_URL\",\n ),\n ModelProviderSpec(\n \"copilot\",\n \"GitHub Copilot\",\n OPENAI_CHAT,\n \"gpt-5.4\",\n \"COPILOT_MODEL\",\n (\"COPILOT_GITHUB_TOKEN\", \"GH_TOKEN\", \"GITHUB_TOKEN\"),\n \"https://api.githubcopilot.com\",\n \"COPILOT_API_BASE_URL\",\n aliases=(\"github-copilot\",),\n ),\n ModelProviderSpec(\n \"bedrock\",\n \"AWS Bedrock\",\n EXTERNAL_RUNTIME,\n \"us.anthropic.claude-sonnet-4-20250514-v1:0\",\n \"BEDROCK_MODEL\",\n (\"AWS_ACCESS_KEY_ID\",),\n \"https://bedrock-runtime.us-east-1.amazonaws.com\",\n \"BEDROCK_BASE_URL\",\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"browser-use-cloud\",\n \"Browser Use Cloud\",\n EXTERNAL_RUNTIME,\n \"bu-2-0\",\n \"BROWSER_USE_MODEL\",\n (\"BROWSER_USE_API_KEY\",),\n \"https://api.browser-use.com\",\n \"BROWSER_USE_BASE_URL\",\n aliases=(\"browser-use\",),\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"oci\",\n \"Oracle OCI\",\n EXTERNAL_RUNTIME,\n \"meta.llama-3.1-70b-instruct\",\n \"OCI_MODEL\",\n (),\n \"\",\n \"OCI_GENAI_ENDPOINT\",\n aliases=(\"oracle\",),\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"openai-codex\",\n \"OpenAI Codex\",\n EXTERNAL_RUNTIME,\n \"gpt-5.5\",\n \"CODEX_MODEL\",\n (\"OPENAI_API_KEY\",),\n \"https://chatgpt.com/backend-api/codex\",\n \"HERMES_CODEX_BASE_URL\",\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"xai-oauth\",\n \"xAI Grok OAuth\",\n EXTERNAL_RUNTIME,\n \"grok-4.3\",\n \"XAI_MODEL\",\n (\"XAI_OAUTH_TOKEN\",),\n \"https://api.x.ai/v1\",\n \"XAI_BASE_URL\",\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"qwen-oauth\",\n \"Qwen OAuth\",\n EXTERNAL_RUNTIME,\n \"qwen3-coder-plus\",\n \"QWEN_MODEL\",\n (\"QWEN_OAUTH_TOKEN\",),\n \"https://portal.qwen.ai/v1\",\n \"HERMES_QWEN_BASE_URL\",\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"google-gemini-cli\",\n \"Google Gemini OAuth\",\n EXTERNAL_RUNTIME,\n \"gemini-3-pro-preview\",\n \"GEMINI_MODEL\",\n (\"GEMINI_OAUTH_TOKEN\",),\n \"cloudcode-pa://google\",\n \"\",\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"copilot-acp\",\n \"GitHub Copilot ACP\",\n EXTERNAL_RUNTIME,\n \"copilot-acp\",\n \"COPILOT_ACP_MODEL\",\n (\"COPILOT_GITHUB_TOKEN\", \"GH_TOKEN\", \"GITHUB_TOKEN\"),\n \"acp://copilot\",\n \"COPILOT_ACP_BASE_URL\",\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"minimax-oauth\",\n \"MiniMax OAuth\",\n EXTERNAL_RUNTIME,\n \"minimax-m3\",\n \"MINIMAX_MODEL\",\n (\"MINIMAX_OAUTH_TOKEN\",),\n \"https://api.minimax.io/anthropic\",\n \"MINIMAX_BASE_URL\",\n allow_openai_fallback=False,\n ),\n)\n\n\n_PROVIDER_BY_ID = {provider.provider_id: provider for provider in MODEL_PROVIDER_REGISTRY}\n_ALIASES = {\n alias: provider.provider_id\n for provider in MODEL_PROVIDER_REGISTRY\n for alias in provider.aliases\n}\n_ALIASES.update({\"grok\": \"xai\", \"openai-api\": \"openai-responses\"})\n\n\nMODEL_PROVIDER_CHOICES: tuple[str, ...] = (\n \"auto\",\n *tuple(provider.provider_id for provider in MODEL_PROVIDER_REGISTRY),\n *tuple(alias for alias in sorted(_ALIASES) if alias not in _PROVIDER_BY_ID),\n)\n\n\ndef normalize_model_provider(provider: str) -> str:\n normalized = str(provider or \"auto\").strip().lower()\n if not normalized:\n return \"auto\"\n return _ALIASES.get(normalized, normalized)\n\n\ndef model_provider_spec(provider: str) -> ModelProviderSpec:\n normalized = normalize_model_provider(provider)\n try:\n return _PROVIDER_BY_ID[normalized]\n except KeyError as exc:\n raise ValueError(f\"Unknown model provider: {provider}\") from exc\n\n\ndef configured_api_key_env(spec: ModelProviderSpec, override: str | None = None) -> str:\n if override and str(override).strip():\n return str(override).strip()\n return spec.primary_api_key_env\n\n\ndef configured_base_url(spec: ModelProviderSpec, override: str | None = None) -> str:\n if override:\n return str(override).strip()\n if spec.base_url_env and os.environ.get(spec.base_url_env):\n return os.environ[spec.base_url_env].strip()\n if spec.provider_id == \"ollama\":\n return os.environ.get(\"LOCAL_LLM_BASE_URL\", \"\").strip() or spec.default_base_url\n return spec.default_base_url\n\n\ndef provider_has_credentials(spec: ModelProviderSpec) -> bool:\n base_url = configured_base_url(spec)\n if spec.transport == OPENAI_CHAT and _is_loopback_url(base_url):\n return True\n if not base_url and not spec.default_base_url:\n return False\n return any(os.environ.get(env_name) for env_name in spec.api_key_envs)\n\n\ndef _is_loopback_url(value: str) -> bool:\n normalized = str(value or \"\").strip().lower()\n return \"localhost\" in normalized or \"127.0.0.1\" in normalized or \"[::1]\" in normalized\n"}, "0f91a0d2387b42a7fd6f41a54687733d878010f6": {"t": 1785756640.0758276, "n": 15414, "h": "feec3bb7009cb605", "c": "from __future__ import annotations\n\nfrom dataclasses import dataclass\nimport os\n\n\n@dataclass(frozen=True, slots=True)\nclass ModelProviderSpec:\n provider_id: str\n label: str\n transport: str\n default_model: str\n model_env: str\n api_key_envs: tuple[str, ...]\n default_base_url: str\n base_url_env: str = \"\"\n aliases: tuple[str, ...] = ()\n allow_openai_fallback: bool = True\n\n @property\n def primary_api_key_env(self) -> str:\n return self.api_key_envs[0] if self.api_key_envs else \"\"\n\n\nOPENAI_RESPONSES = \"openai_responses\"\nOPENAI_CHAT = \"openai_chat\"\nANTHROPIC_MESSAGES = \"anthropic_messages\"\nEXTERNAL_RUNTIME = \"external_runtime\"\n\n\nMODEL_PROVIDER_REGISTRY: tuple[ModelProviderSpec, ...] = (\n ModelProviderSpec(\n \"openai-responses\",\n \"OpenAI Responses\",\n OPENAI_RESPONSES,\n \"gpt-5-mini\",\n \"OPENAI_MODEL\",\n (\"OPENAI_API_KEY\",),\n \"https://api.openai.com/v1\",\n \"OPENAI_BASE_URL\",\n aliases=(\"openai\", \"openai-api\"),\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"openai-chat\",\n \"OpenAI Chat\",\n OPENAI_CHAT,\n \"gpt-5-mini\",\n \"OPENAI_MODEL\",\n (\"OPENAI_API_KEY\",),\n \"https://api.openai.com/v1\",\n \"OPENAI_BASE_URL\",\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"openrouter\",\n \"OpenRouter\",\n OPENAI_CHAT,\n \"anthropic/claude-sonnet-4.6\",\n \"OPENROUTER_MODEL\",\n (\"OPENROUTER_API_KEY\",),\n \"https://openrouter.ai/api/v1\",\n \"OPENROUTER_BASE_URL\",\n ),\n ModelProviderSpec(\n \"nous\",\n \"Nous Portal\",\n OPENAI_CHAT,\n \"moonshotai/kimi-k2.6\",\n \"NOUS_MODEL\",\n (\"NOUS_API_KEY\", \"NOUS_INFERENCE_API_KEY\"),\n \"https://inference-api.nousresearch.com/v1\",\n \"NOUS_INFERENCE_BASE_URL\",\n ),\n ModelProviderSpec(\n \"novita\",\n \"NovitaAI\",\n OPENAI_CHAT,\n \"deepseek/deepseek-r1\",\n \"NOVITA_MODEL\",\n (\"NOVITA_API_KEY\",),\n \"https://api.novita.ai/openai/v1\",\n \"NOVITA_BASE_URL\",\n ),\n ModelProviderSpec(\n \"lmstudio\",\n \"LM Studio\",\n OPENAI_CHAT,\n \"local-model\",\n \"LM_MODEL\",\n (\"LM_API_KEY\",),\n \"http://127.0.0.1:1234/v1\",\n \"LM_BASE_URL\",\n ),\n ModelProviderSpec(\n \"anthropic\",\n \"Anthropic\",\n ANTHROPIC_MESSAGES,\n \"claude-sonnet-4-6\",\n \"ANTHROPIC_MODEL\",\n (\"ANTHROPIC_API_KEY\", \"ANTHROPIC_TOKEN\", \"CLAUDE_CODE_OAUTH_TOKEN\"),\n \"https://api.anthropic.com\",\n \"ANTHROPIC_BASE_URL\",\n ),\n ModelProviderSpec(\n \"alibaba\",\n \"Qwen Cloud\",\n OPENAI_CHAT,\n \"qwen-max\",\n \"DASHSCOPE_MODEL\",\n (\"DASHSCOPE_API_KEY\",),\n \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1\",\n \"DASHSCOPE_BASE_URL\",\n aliases=(\"qwen\", \"dashscope\"),\n ),\n ModelProviderSpec(\n \"xai\",\n \"xAI Grok\",\n OPENAI_CHAT,\n \"grok-4.3\",\n \"XAI_MODEL\",\n (\"XAI_API_KEY\",),\n \"https://api.x.ai/v1\",\n \"XAI_BASE_URL\",\n aliases=(\"grok\", \"x-ai\"),\n ),\n ModelProviderSpec(\n \"gemini\",\n \"Google Gemini\",\n OPENAI_CHAT,\n \"gemini-3-pro-preview\",\n \"GEMINI_MODEL\",\n (\"GOOGLE_API_KEY\", \"GEMINI_API_KEY\"),\n \"https://generativelanguage.googleapis.com/v1beta/openai\",\n \"GEMINI_BASE_URL\",\n aliases=(\"google\", \"google-ai-studio\"),\n ),\n ModelProviderSpec(\n \"deepseek\",\n \"DeepSeek\",\n OPENAI_CHAT,\n \"deepseek-chat\",\n \"DEEPSEEK_MODEL\",\n (\"DEEPSEEK_API_KEY\",),\n \"https://api.deepseek.com/v1\",\n \"DEEPSEEK_BASE_URL\",\n ),\n ModelProviderSpec(\n \"mistral\",\n \"Mistral\",\n OPENAI_CHAT,\n \"mistral-large-latest\",\n \"MISTRAL_MODEL\",\n (\"MISTRAL_API_KEY\",),\n \"https://api.mistral.ai/v1\",\n \"MISTRAL_BASE_URL\",\n ),\n ModelProviderSpec(\n \"groq\",\n \"Groq\",\n OPENAI_CHAT,\n \"llama-3.3-70b-versatile\",\n \"GROQ_MODEL\",\n (\"GROQ_API_KEY\",),\n \"https://api.groq.com/openai/v1\",\n \"GROQ_BASE_URL\",\n ),\n ModelProviderSpec(\n \"cerebras\",\n \"Cerebras\",\n OPENAI_CHAT,\n \"llama3.3-70b\",\n \"CEREBRAS_MODEL\",\n (\"CEREBRAS_API_KEY\",),\n \"https://api.cerebras.ai/v1\",\n \"CEREBRAS_BASE_URL\",\n ),\n ModelProviderSpec(\n \"ollama\",\n \"Ollama\",\n OPENAI_CHAT,\n \"llama3.1\",\n \"OLLAMA_MODEL\",\n (\"OLLAMA_API_KEY\",),\n \"http://127.0.0.1:11434/v1\",\n \"OLLAMA_BASE_URL\",\n ),\n ModelProviderSpec(\n \"ollama-cloud\",\n \"Ollama Cloud\",\n OPENAI_CHAT,\n \"gpt-oss:20b\",\n \"OLLAMA_CLOUD_MODEL\",\n (\"OLLAMA_API_KEY\",),\n \"https://ollama.com/v1\",\n \"OLLAMA_CLOUD_BASE_URL\",\n ),\n ModelProviderSpec(\n \"local-openai\",\n \"Local OpenAI-compatible\",\n OPENAI_CHAT,\n \"llama3.1\",\n \"LOCAL_LLM_MODEL\",\n (\"LOCAL_LLM_API_KEY\",),\n \"http://127.0.0.1:11434/v1\",\n \"LOCAL_LLM_BASE_URL\",\n ),\n ModelProviderSpec(\n \"vercel\",\n \"Vercel AI Gateway\",\n OPENAI_CHAT,\n \"anthropic/claude-sonnet-4.6\",\n \"AI_GATEWAY_MODEL\",\n (\"AI_GATEWAY_API_KEY\", \"VERCEL_OIDC_TOKEN\"),\n \"https://ai-gateway.vercel.sh/v1\",\n \"AI_GATEWAY_BASE_URL\",\n aliases=(\"ai-gateway\", \"vercel-ai-gateway\"),\n ),\n ModelProviderSpec(\n \"litellm\",\n \"LiteLLM\",\n OPENAI_CHAT,\n \"openai/gpt-5-mini\",\n \"LITELLM_MODEL\",\n (\"LITELLM_API_KEY\",),\n \"http://localhost:4000/v1\",\n \"LITELLM_BASE_URL\",\n ),\n ModelProviderSpec(\n \"nvidia\",\n \"NVIDIA NIM\",\n OPENAI_CHAT,\n \"nvidia/nemotron-3-super-120b-a12b\",\n \"NVIDIA_MODEL\",\n (\"NVIDIA_API_KEY\",),\n \"https://integrate.api.nvidia.com/v1\",\n \"NVIDIA_BASE_URL\",\n aliases=(\"nim\",),\n ),\n ModelProviderSpec(\n \"huggingface\",\n \"Hugging Face\",\n OPENAI_CHAT,\n \"meta-llama/Llama-3.3-70B-Instruct\",\n \"HF_MODEL\",\n (\"HF_TOKEN\", \"HUGGINGFACE_API_KEY\"),\n \"https://router.huggingface.co/v1\",\n \"HF_BASE_URL\",\n aliases=(\"hf\",),\n ),\n ModelProviderSpec(\n \"zai\",\n \"Z.AI / GLM\",\n OPENAI_CHAT,\n \"glm-5.1\",\n \"GLM_MODEL\",\n (\"GLM_API_KEY\", \"ZAI_API_KEY\", \"Z_AI_API_KEY\"),\n \"https://api.z.ai/api/paas/v4\",\n \"GLM_BASE_URL\",\n aliases=(\"z-ai\", \"glm\", \"zhipu\"),\n ),\n ModelProviderSpec(\n \"kimi-coding\",\n \"Kimi / Moonshot\",\n OPENAI_CHAT,\n \"kimi-k2.6\",\n \"KIMI_MODEL\",\n (\"KIMI_API_KEY\", \"KIMI_CODING_API_KEY\"),\n \"https://api.moonshot.ai/v1\",\n \"KIMI_BASE_URL\",\n aliases=(\"moonshot\", \"kimi\"),\n ),\n ModelProviderSpec(\n \"kimi-coding-cn\",\n \"Kimi / Moonshot China\",\n OPENAI_CHAT,\n \"kimi-k2.6\",\n \"KIMI_CN_MODEL\",\n (\"KIMI_CN_API_KEY\",),\n \"https://api.moonshot.cn/v1\",\n \"KIMI_CN_BASE_URL\",\n ),\n ModelProviderSpec(\n \"stepfun\",\n \"StepFun Step Plan\",\n OPENAI_CHAT,\n \"step-3.7-flash\",\n \"STEPFUN_MODEL\",\n (\"STEPFUN_API_KEY\",),\n \"https://api.stepfun.ai/step_plan/v1\",\n \"STEPFUN_BASE_URL\",\n ),\n ModelProviderSpec(\n \"minimax\",\n \"MiniMax\",\n ANTHROPIC_MESSAGES,\n \"minimax-m3\",\n \"MINIMAX_MODEL\",\n (\"MINIMAX_API_KEY\",),\n \"https://api.minimax.io/anthropic\",\n \"MINIMAX_BASE_URL\",\n ),\n ModelProviderSpec(\n \"minimax-cn\",\n \"MiniMax China\",\n ANTHROPIC_MESSAGES,\n \"minimax-m3\",\n \"MINIMAX_CN_MODEL\",\n (\"MINIMAX_CN_API_KEY\",),\n \"https://api.minimaxi.com/anthropic\",\n \"MINIMAX_CN_BASE_URL\",\n ),\n ModelProviderSpec(\n \"arcee\",\n \"Arcee AI\",\n OPENAI_CHAT,\n \"auto\",\n \"ARCEE_MODEL\",\n (\"ARCEEAI_API_KEY\", \"ARCEE_API_KEY\"),\n \"https://api.arcee.ai/api/v1\",\n \"ARCEE_BASE_URL\",\n ),\n ModelProviderSpec(\n \"gmi\",\n \"GMI Cloud\",\n OPENAI_CHAT,\n \"auto\",\n \"GMI_MODEL\",\n (\"GMI_API_KEY\",),\n \"https://api.gmi-serving.com/v1\",\n \"GMI_BASE_URL\",\n ),\n ModelProviderSpec(\n \"xiaomi\",\n \"Xiaomi MiMo\",\n OPENAI_CHAT,\n \"mimo-v2.5-pro\",\n \"XIAOMI_MODEL\",\n (\"XIAOMI_API_KEY\",),\n \"https://api.xiaomimimo.com/v1\",\n \"XIAOMI_BASE_URL\",\n ),\n ModelProviderSpec(\n \"tencent-tokenhub\",\n \"Tencent TokenHub\",\n OPENAI_CHAT,\n \"tencent/hy3-preview\",\n \"TOKENHUB_MODEL\",\n (\"TOKENHUB_API_KEY\",),\n \"https://tokenhub.tencentmaas.com/v1\",\n \"TOKENHUB_BASE_URL\",\n aliases=(\"tokenhub\", \"tencent\"),\n ),\n ModelProviderSpec(\n \"opencode-zen\",\n \"OpenCode Zen\",\n OPENAI_CHAT,\n \"anthropic/claude-sonnet-4.6\",\n \"OPENCODE_ZEN_MODEL\",\n (\"OPENCODE_ZEN_API_KEY\",),\n \"https://opencode.ai/zen/v1\",\n \"OPENCODE_ZEN_BASE_URL\",\n ),\n ModelProviderSpec(\n \"opencode-go\",\n \"OpenCode Go\",\n OPENAI_CHAT,\n \"z-ai/glm-5.1\",\n \"OPENCODE_GO_MODEL\",\n (\"OPENCODE_GO_API_KEY\",),\n \"https://opencode.ai/zen/go/v1\",\n \"OPENCODE_GO_BASE_URL\",\n ),\n ModelProviderSpec(\n \"kilocode\",\n \"Kilo Code\",\n OPENAI_CHAT,\n \"anthropic/claude-sonnet-4.6\",\n \"KILOCODE_MODEL\",\n (\"KILOCODE_API_KEY\",),\n \"https://api.kilo.ai/api/gateway\",\n \"KILOCODE_BASE_URL\",\n ),\n ModelProviderSpec(\n \"azure-openai\",\n \"Azure OpenAI\",\n OPENAI_CHAT,\n \"gpt-5\",\n \"AZURE_OPENAI_MODEL\",\n (\"AZURE_OPENAI_API_KEY\", \"AZURE_OPENAI_KEY\"),\n \"\",\n \"AZURE_OPENAI_ENDPOINT\",\n aliases=(\"azure\",),\n ),\n ModelProviderSpec(\n \"azure-foundry\",\n \"Azure Foundry\",\n OPENAI_CHAT,\n \"gpt-5\",\n \"AZURE_FOUNDRY_MODEL\",\n (\"AZURE_FOUNDRY_API_KEY\",),\n \"\",\n \"AZURE_FOUNDRY_BASE_URL\",\n ),\n ModelProviderSpec(\n \"copilot\",\n \"GitHub Copilot\",\n OPENAI_CHAT,\n \"gpt-5.4\",\n \"COPILOT_MODEL\",\n (\"COPILOT_GITHUB_TOKEN\", \"GH_TOKEN\", \"GITHUB_TOKEN\"),\n \"https://api.githubcopilot.com\",\n \"COPILOT_API_BASE_URL\",\n aliases=(\"github-copilot\",),\n ),\n ModelProviderSpec(\n \"bedrock\",\n \"AWS Bedrock\",\n EXTERNAL_RUNTIME,\n \"us.anthropic.claude-sonnet-4-20250514-v1:0\",\n \"BEDROCK_MODEL\",\n (\"AWS_ACCESS_KEY_ID\",),\n \"https://bedrock-runtime.us-east-1.amazonaws.com\",\n \"BEDROCK_BASE_URL\",\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"browser-use-cloud\",\n \"Browser Use Cloud\",\n EXTERNAL_RUNTIME,\n \"bu-2-0\",\n \"BROWSER_USE_MODEL\",\n (\"BROWSER_USE_API_KEY\",),\n \"https://api.browser-use.com\",\n \"BROWSER_USE_BASE_URL\",\n aliases=(\"browser-use\",),\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"oci\",\n \"Oracle OCI\",\n EXTERNAL_RUNTIME,\n \"meta.llama-3.1-70b-instruct\",\n \"OCI_MODEL\",\n (),\n \"\",\n \"OCI_GENAI_ENDPOINT\",\n aliases=(\"oracle\",),\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"openai-codex\",\n \"OpenAI Codex\",\n EXTERNAL_RUNTIME,\n \"gpt-5.5\",\n \"CODEX_MODEL\",\n (\"OPENAI_API_KEY\",),\n \"https://chatgpt.com/backend-api/codex\",\n \"HERMES_CODEX_BASE_URL\",\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"xai-oauth\",\n \"xAI Grok OAuth\",\n EXTERNAL_RUNTIME,\n \"grok-4.3\",\n \"XAI_MODEL\",\n (\"XAI_OAUTH_TOKEN\",),\n \"https://api.x.ai/v1\",\n \"XAI_BASE_URL\",\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"qwen-oauth\",\n \"Qwen OAuth\",\n EXTERNAL_RUNTIME,\n \"qwen3-coder-plus\",\n \"QWEN_MODEL\",\n (\"QWEN_OAUTH_TOKEN\",),\n \"https://portal.qwen.ai/v1\",\n \"HERMES_QWEN_BASE_URL\",\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"google-gemini-cli\",\n \"Google Gemini OAuth\",\n EXTERNAL_RUNTIME,\n \"gemini-3-pro-preview\",\n \"GEMINI_MODEL\",\n (\"GEMINI_OAUTH_TOKEN\",),\n \"cloudcode-pa://google\",\n \"\",\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"copilot-acp\",\n \"GitHub Copilot ACP\",\n EXTERNAL_RUNTIME,\n \"copilot-acp\",\n \"COPILOT_ACP_MODEL\",\n (\"COPILOT_GITHUB_TOKEN\", \"GH_TOKEN\", \"GITHUB_TOKEN\"),\n \"acp://copilot\",\n \"COPILOT_ACP_BASE_URL\",\n allow_openai_fallback=False,\n ),\n ModelProviderSpec(\n \"minimax-oauth\",\n \"MiniMax OAuth\",\n EXTERNAL_RUNTIME,\n \"minimax-m3\",\n \"MINIMAX_MODEL\",\n (\"MINIMAX_OAUTH_TOKEN\",),\n \"https://api.minimax.io/anthropic\",\n \"MINIMAX_BASE_URL\",\n allow_openai_fallback=False,\n ),\n)\n\n\n_PROVIDER_BY_ID = {provider.provider_id: provider for provider in MODEL_PROVIDER_REGISTRY}\n_ALIASES = {\n alias: provider.provider_id\n for provider in MODEL_PROVIDER_REGISTRY\n for alias in provider.aliases\n}\n_ALIASES.update({\"grok\": \"xai\", \"openai-api\": \"openai-responses\"})\n\n\nMODEL_PROVIDER_CHOICES: tuple[str, ...] = (\n \"auto\",\n *tuple(provider.provider_id for provider in MODEL_PROVIDER_REGISTRY),\n *tuple(alias for alias in sorted(_ALIASES) if alias not in _PROVIDER_BY_ID),\n)\n\n\ndef normalize_model_provider(provider: str) -> str:\n normalized = str(provider or \"auto\").strip().lower()\n if not normalized:\n return \"auto\"\n return _ALIASES.get(normalized, normalized)\n\n\ndef model_provider_spec(provider: str) -> ModelProviderSpec:\n normalized = normalize_model_provider(provider)\n try:\n return _PROVIDER_BY_ID[normalized]\n except KeyError as exc:\n raise ValueError(f\"Unknown model provider: {provider}\") from exc\n\n\ndef configured_api_key_env(spec: ModelProviderSpec, override: str | None = None) -> str:\n if override and str(override).strip():\n return str(override).strip()\n return spec.primary_api_key_env\n\n\ndef configured_base_url(spec: ModelProviderSpec, override: str | None = None) -> str:\n if override:\n return str(override).strip()\n if spec.base_url_env and os.environ.get(spec.base_url_env):\n return os.environ[spec.base_url_env].strip()\n if spec.provider_id == \"ollama\":\n return os.environ.get(\"LOCAL_LLM_BASE_URL\", \"\").strip() or spec.default_base_url\n return spec.default_base_url\n\n\ndef provider_has_credentials(spec: ModelProviderSpec) -> bool:\n base_url = configured_base_url(spec)\n if spec.transport == OPENAI_CHAT and _is_loopback_url(base_url):\n return True\n if not base_url and not spec.default_base_url:\n return False\n return any(os.environ.get(env_name) for env_name in spec.api_key_envs)\n\n\ndef _is_loopback_url(value: str) -> bool:\n normalized = str(value or \"\").strip().lower()\n return \"localhost\" in normalized or \"127.0.0.1\" in normalized or \"[::1]\" in normalized\n"}}, "apps/windows/Humungousaur.App/Models/AppRuntimeDefaults.cs": {"15d8b78fa52cafc8c0192e523f8a5eba43c7963b": {"t": 1785745800.1898067, "n": 3662, "h": "cd2ddd9e92b857d2", "c": "namespace Humungousaur.App.Models;\n\npublic static class AppRuntimeDefaults\n{\n public const string Planner = \"model\";\n public const string ModelProvider = \"openai\";\n public const string ModelName = \"gpt-5-mini\";\n public const string TtsProvider = \"system\";\n public const string ConnectorOAuthBrokerUrl = \"https://humungousaur-connector-oauth.humungousaur.workers.dev\";\n\n public static string EffectivePlanner(string planner)\n {\n return string.IsNullOrWhiteSpace(planner) ? Planner : planner.Trim();\n }\n\n public static string EffectiveModelProvider(string provider)\n {\n return string.IsNullOrWhiteSpace(provider) ? ModelProvider : provider.Trim();\n }\n\n public static string EffectiveModelName(string modelName)\n {\n return string.IsNullOrWhiteSpace(modelName) ? ModelName : modelName.Trim();\n }\n\n public static string EffectiveJanusModelProvider(string provider)\n {\n return string.IsNullOrWhiteSpace(provider) ? \"same-as-main\" : provider.Trim();\n }\n\n public static string EffectiveTtsProvider(string provider)\n {\n return string.IsNullOrWhiteSpace(provider) ? TtsProvider : provider.Trim();\n }\n\n public static string ModelApiKeyName(string provider)\n {\n return EffectiveModelProvider(provider) switch\n {\n \"openai\" or \"openai-responses\" or \"openai-chat\" => \"OPENAI_API_KEY\",\n \"openrouter\" => \"OPENROUTER_API_KEY\",\n \"nous\" => \"NOUS_API_KEY\",\n \"novita\" => \"NOVITA_API_KEY\",\n \"lmstudio\" => \"LM_API_KEY\",\n \"anthropic\" => \"ANTHROPIC_API_KEY\",\n \"alibaba\" => \"DASHSCOPE_API_KEY\",\n \"groq\" => \"GROQ_API_KEY\",\n \"grok\" or \"xai\" => \"XAI_API_KEY\",\n \"gemini\" => \"GOOGLE_API_KEY\",\n \"deepseek\" => \"DEEPSEEK_API_KEY\",\n \"mistral\" => \"MISTRAL_API_KEY\",\n \"cerebras\" => \"CEREBRAS_API_KEY\",\n \"ollama\" => \"OLLAMA_API_KEY\",\n \"ollama-cloud\" => \"OLLAMA_API_KEY\",\n \"local-openai\" => \"LOCAL_LLM_API_KEY\",\n \"vercel\" => \"AI_GATEWAY_API_KEY\",\n \"litellm\" => \"LITELLM_API_KEY\",\n \"nvidia\" => \"NVIDIA_API_KEY\",\n \"huggingface\" => \"HF_TOKEN\",\n \"zai\" => \"GLM_API_KEY\",\n \"kimi-coding\" => \"KIMI_API_KEY\",\n \"kimi-coding-cn\" => \"KIMI_CN_API_KEY\",\n \"stepfun\" => \"STEPFUN_API_KEY\",\n \"minimax\" => \"MINIMAX_API_KEY\",\n \"minimax-cn\" => \"MINIMAX_CN_API_KEY\",\n \"arcee\" => \"ARCEEAI_API_KEY\",\n \"gmi\" => \"GMI_API_KEY\",\n \"xiaomi\" => \"XIAOMI_API_KEY\",\n \"tencent-tokenhub\" => \"TOKENHUB_API_KEY\",\n \"opencode-zen\" => \"OPENCODE_ZEN_API_KEY\",\n \"opencode-go\" => \"OPENCODE_GO_API_KEY\",\n \"kilocode\" => \"KILOCODE_API_KEY\",\n \"azure-openai\" => \"AZURE_OPENAI_API_KEY\",\n \"azure-foundry\" => \"AZURE_FOUNDRY_API_KEY\",\n \"copilot\" or \"copilot-acp\" => \"GITHUB_TOKEN\",\n \"bedrock\" => \"AWS_ACCESS_KEY_ID\",\n \"browser-use-cloud\" => \"BROWSER_USE_API_KEY\",\n _ => \"OPENAI_API_KEY\",\n };\n }\n\n public static string CliModelProvider(string provider)\n {\n return EffectiveModelProvider(provider) switch\n {\n \"openai\" => \"openai-responses\",\n var value => value,\n };\n }\n\n public static string CliJanusModelProvider(string provider)\n {\n return EffectiveJanusModelProvider(provider) switch\n {\n \"same-as-main\" => \"same-as-main\",\n \"openai\" => \"openai-responses\",\n var value => value,\n };\n }\n}\n"}, "10fede7ab8163961916ac9b545000527d92abb03": {"t": 1785756639.541343, "n": 3662, "h": "cd2ddd9e92b857d2", "c": "namespace Humungousaur.App.Models;\n\npublic static class AppRuntimeDefaults\n{\n public const string Planner = \"model\";\n public const string ModelProvider = \"openai\";\n public const string ModelName = \"gpt-5-mini\";\n public const string TtsProvider = \"system\";\n public const string ConnectorOAuthBrokerUrl = \"https://humungousaur-connector-oauth.humungousaur.workers.dev\";\n\n public static string EffectivePlanner(string planner)\n {\n return string.IsNullOrWhiteSpace(planner) ? Planner : planner.Trim();\n }\n\n public static string EffectiveModelProvider(string provider)\n {\n return string.IsNullOrWhiteSpace(provider) ? ModelProvider : provider.Trim();\n }\n\n public static string EffectiveModelName(string modelName)\n {\n return string.IsNullOrWhiteSpace(modelName) ? ModelName : modelName.Trim();\n }\n\n public static string EffectiveJanusModelProvider(string provider)\n {\n return string.IsNullOrWhiteSpace(provider) ? \"same-as-main\" : provider.Trim();\n }\n\n public static string EffectiveTtsProvider(string provider)\n {\n return string.IsNullOrWhiteSpace(provider) ? TtsProvider : provider.Trim();\n }\n\n public static string ModelApiKeyName(string provider)\n {\n return EffectiveModelProvider(provider) switch\n {\n \"openai\" or \"openai-responses\" or \"openai-chat\" => \"OPENAI_API_KEY\",\n \"openrouter\" => \"OPENROUTER_API_KEY\",\n \"nous\" => \"NOUS_API_KEY\",\n \"novita\" => \"NOVITA_API_KEY\",\n \"lmstudio\" => \"LM_API_KEY\",\n \"anthropic\" => \"ANTHROPIC_API_KEY\",\n \"alibaba\" => \"DASHSCOPE_API_KEY\",\n \"groq\" => \"GROQ_API_KEY\",\n \"grok\" or \"xai\" => \"XAI_API_KEY\",\n \"gemini\" => \"GOOGLE_API_KEY\",\n \"deepseek\" => \"DEEPSEEK_API_KEY\",\n \"mistral\" => \"MISTRAL_API_KEY\",\n \"cerebras\" => \"CEREBRAS_API_KEY\",\n \"ollama\" => \"OLLAMA_API_KEY\",\n \"ollama-cloud\" => \"OLLAMA_API_KEY\",\n \"local-openai\" => \"LOCAL_LLM_API_KEY\",\n \"vercel\" => \"AI_GATEWAY_API_KEY\",\n \"litellm\" => \"LITELLM_API_KEY\",\n \"nvidia\" => \"NVIDIA_API_KEY\",\n \"huggingface\" => \"HF_TOKEN\",\n \"zai\" => \"GLM_API_KEY\",\n \"kimi-coding\" => \"KIMI_API_KEY\",\n \"kimi-coding-cn\" => \"KIMI_CN_API_KEY\",\n \"stepfun\" => \"STEPFUN_API_KEY\",\n \"minimax\" => \"MINIMAX_API_KEY\",\n \"minimax-cn\" => \"MINIMAX_CN_API_KEY\",\n \"arcee\" => \"ARCEEAI_API_KEY\",\n \"gmi\" => \"GMI_API_KEY\",\n \"xiaomi\" => \"XIAOMI_API_KEY\",\n \"tencent-tokenhub\" => \"TOKENHUB_API_KEY\",\n \"opencode-zen\" => \"OPENCODE_ZEN_API_KEY\",\n \"opencode-go\" => \"OPENCODE_GO_API_KEY\",\n \"kilocode\" => \"KILOCODE_API_KEY\",\n \"azure-openai\" => \"AZURE_OPENAI_API_KEY\",\n \"azure-foundry\" => \"AZURE_FOUNDRY_API_KEY\",\n \"copilot\" or \"copilot-acp\" => \"GITHUB_TOKEN\",\n \"bedrock\" => \"AWS_ACCESS_KEY_ID\",\n \"browser-use-cloud\" => \"BROWSER_USE_API_KEY\",\n _ => \"OPENAI_API_KEY\",\n };\n }\n\n public static string CliModelProvider(string provider)\n {\n return EffectiveModelProvider(provider) switch\n {\n \"openai\" => \"openai-responses\",\n var value => value,\n };\n }\n\n public static string CliJanusModelProvider(string provider)\n {\n return EffectiveJanusModelProvider(provider) switch\n {\n \"same-as-main\" => \"same-as-main\",\n \"openai\" => \"openai-responses\",\n var value => value,\n };\n }\n}\n"}, "5272e1d0a3d25da8abf9dfe0b0dfbf99779f9c00": {"t": 1785756641.4181545, "n": 3547, "h": "725bbc44e167cff3", "c": "namespace Humungousaur.App.Models;\n\npublic static class AppRuntimeDefaults\n{\n public const string Planner = \"model\";\n public const string ModelProvider = \"openai\";\n public const string ModelName = \"gpt-5-mini\";\n public const string TtsProvider = \"system\";\n\n public static string EffectivePlanner(string planner)\n {\n return string.IsNullOrWhiteSpace(planner) ? Planner : planner.Trim();\n }\n\n public static string EffectiveModelProvider(string provider)\n {\n return string.IsNullOrWhiteSpace(provider) ? ModelProvider : provider.Trim();\n }\n\n public static string EffectiveModelName(string modelName)\n {\n return string.IsNullOrWhiteSpace(modelName) ? ModelName : modelName.Trim();\n }\n\n public static string EffectiveJanusModelProvider(string provider)\n {\n return string.IsNullOrWhiteSpace(provider) ? \"same-as-main\" : provider.Trim();\n }\n\n public static string EffectiveTtsProvider(string provider)\n {\n return string.IsNullOrWhiteSpace(provider) ? TtsProvider : provider.Trim();\n }\n\n public static string ModelApiKeyName(string provider)\n {\n return EffectiveModelProvider(provider) switch\n {\n \"openai\" or \"openai-responses\" or \"openai-chat\" => \"OPENAI_API_KEY\",\n \"openrouter\" => \"OPENROUTER_API_KEY\",\n \"nous\" => \"NOUS_API_KEY\",\n \"novita\" => \"NOVITA_API_KEY\",\n \"lmstudio\" => \"LM_API_KEY\",\n \"anthropic\" => \"ANTHROPIC_API_KEY\",\n \"alibaba\" => \"DASHSCOPE_API_KEY\",\n \"groq\" => \"GROQ_API_KEY\",\n \"grok\" or \"xai\" => \"XAI_API_KEY\",\n \"gemini\" => \"GOOGLE_API_KEY\",\n \"deepseek\" => \"DEEPSEEK_API_KEY\",\n \"mistral\" => \"MISTRAL_API_KEY\",\n \"cerebras\" => \"CEREBRAS_API_KEY\",\n \"ollama\" => \"OLLAMA_API_KEY\",\n \"ollama-cloud\" => \"OLLAMA_API_KEY\",\n \"local-openai\" => \"LOCAL_LLM_API_KEY\",\n \"vercel\" => \"AI_GATEWAY_API_KEY\",\n \"litellm\" => \"LITELLM_API_KEY\",\n \"nvidia\" => \"NVIDIA_API_KEY\",\n \"huggingface\" => \"HF_TOKEN\",\n \"zai\" => \"GLM_API_KEY\",\n \"kimi-coding\" => \"KIMI_API_KEY\",\n \"kimi-coding-cn\" => \"KIMI_CN_API_KEY\",\n \"stepfun\" => \"STEPFUN_API_KEY\",\n \"minimax\" => \"MINIMAX_API_KEY\",\n \"minimax-cn\" => \"MINIMAX_CN_API_KEY\",\n \"arcee\" => \"ARCEEAI_API_KEY\",\n \"gmi\" => \"GMI_API_KEY\",\n \"xiaomi\" => \"XIAOMI_API_KEY\",\n \"tencent-tokenhub\" => \"TOKENHUB_API_KEY\",\n \"opencode-zen\" => \"OPENCODE_ZEN_API_KEY\",\n \"opencode-go\" => \"OPENCODE_GO_API_KEY\",\n \"kilocode\" => \"KILOCODE_API_KEY\",\n \"azure-openai\" => \"AZURE_OPENAI_API_KEY\",\n \"azure-foundry\" => \"AZURE_FOUNDRY_API_KEY\",\n \"copilot\" or \"copilot-acp\" => \"GITHUB_TOKEN\",\n \"bedrock\" => \"AWS_ACCESS_KEY_ID\",\n \"browser-use-cloud\" => \"BROWSER_USE_API_KEY\",\n _ => \"OPENAI_API_KEY\",\n };\n }\n\n public static string CliModelProvider(string provider)\n {\n return EffectiveModelProvider(provider) switch\n {\n \"openai\" => \"openai-responses\",\n var value => value,\n };\n }\n\n public static string CliJanusModelProvider(string provider)\n {\n return EffectiveJanusModelProvider(provider) switch\n {\n \"same-as-main\" => \"same-as-main\",\n \"openai\" => \"openai-responses\",\n var value => value,\n };\n }\n}\n"}, "8d082d4caf3cfac1a7f0f329ec876366f53df68c": {"t": 1785756642.5315516, "n": 3550, "h": "af07ffc5869daf94", "c": "namespace Humungousaur.App.Models;\n\npublic static class AppRuntimeDefaults\n{\n public const string Planner = \"model\";\n public const string ModelProvider = \"openai\";\n public const string ModelName = \"gpt-5-mini\";\n public const string TtsProvider = \"system\";\n\n public static string EffectivePlanner(string planner)\n {\n return string.IsNullOrWhiteSpace(planner) ? Planner : planner.Trim();\n }\n\n public static string EffectiveModelProvider(string provider)\n {\n return string.IsNullOrWhiteSpace(provider) ? ModelProvider : provider.Trim();\n }\n\n public static string EffectiveModelName(string modelName)\n {\n return string.IsNullOrWhiteSpace(modelName) ? ModelName : modelName.Trim();\n }\n\n public static string EffectiveActiveModelProvider(string provider)\n {\n return string.IsNullOrWhiteSpace(provider) ? \"same-as-main\" : provider.Trim();\n }\n\n public static string EffectiveTtsProvider(string provider)\n {\n return string.IsNullOrWhiteSpace(provider) ? TtsProvider : provider.Trim();\n }\n\n public static string ModelApiKeyName(string provider)\n {\n return EffectiveModelProvider(provider) switch\n {\n \"openai\" or \"openai-responses\" or \"openai-chat\" => \"OPENAI_API_KEY\",\n \"openrouter\" => \"OPENROUTER_API_KEY\",\n \"nous\" => \"NOUS_API_KEY\",\n \"novita\" => \"NOVITA_API_KEY\",\n \"lmstudio\" => \"LM_API_KEY\",\n \"anthropic\" => \"ANTHROPIC_API_KEY\",\n \"alibaba\" => \"DASHSCOPE_API_KEY\",\n \"groq\" => \"GROQ_API_KEY\",\n \"grok\" or \"xai\" => \"XAI_API_KEY\",\n \"gemini\" => \"GOOGLE_API_KEY\",\n \"deepseek\" => \"DEEPSEEK_API_KEY\",\n \"mistral\" => \"MISTRAL_API_KEY\",\n \"cerebras\" => \"CEREBRAS_API_KEY\",\n \"ollama\" => \"OLLAMA_API_KEY\",\n \"ollama-cloud\" => \"OLLAMA_API_KEY\",\n \"local-openai\" => \"LOCAL_LLM_API_KEY\",\n \"vercel\" => \"AI_GATEWAY_API_KEY\",\n \"litellm\" => \"LITELLM_API_KEY\",\n \"nvidia\" => \"NVIDIA_API_KEY\",\n \"huggingface\" => \"HF_TOKEN\",\n \"zai\" => \"GLM_API_KEY\",\n \"kimi-coding\" => \"KIMI_API_KEY\",\n \"kimi-coding-cn\" => \"KIMI_CN_API_KEY\",\n \"stepfun\" => \"STEPFUN_API_KEY\",\n \"minimax\" => \"MINIMAX_API_KEY\",\n \"minimax-cn\" => \"MINIMAX_CN_API_KEY\",\n \"arcee\" => \"ARCEEAI_API_KEY\",\n \"gmi\" => \"GMI_API_KEY\",\n \"xiaomi\" => \"XIAOMI_API_KEY\",\n \"tencent-tokenhub\" => \"TOKENHUB_API_KEY\",\n \"opencode-zen\" => \"OPENCODE_ZEN_API_KEY\",\n \"opencode-go\" => \"OPENCODE_GO_API_KEY\",\n \"kilocode\" => \"KILOCODE_API_KEY\",\n \"azure-openai\" => \"AZURE_OPENAI_API_KEY\",\n \"azure-foundry\" => \"AZURE_FOUNDRY_API_KEY\",\n \"copilot\" or \"copilot-acp\" => \"GITHUB_TOKEN\",\n \"bedrock\" => \"AWS_ACCESS_KEY_ID\",\n \"browser-use-cloud\" => \"BROWSER_USE_API_KEY\",\n _ => \"OPENAI_API_KEY\",\n };\n }\n\n public static string CliModelProvider(string provider)\n {\n return EffectiveModelProvider(provider) switch\n {\n \"openai\" => \"openai-responses\",\n var value => value,\n };\n }\n\n public static string CliActiveModelProvider(string provider)\n {\n return EffectiveActiveModelProvider(provider) switch\n {\n \"same-as-main\" => \"same-as-main\",\n \"openai\" => \"openai-responses\",\n var value => value,\n };\n }\n}\n"}, "0f91a0d2387b42a7fd6f41a54687733d878010f6": {"t": 1785756644.4082265, "n": 3097, "h": "b4cd8d9b733c830c", "c": "namespace Humungousaur.App.Models;\n\npublic static class AppRuntimeDefaults\n{\n public const string Planner = \"model\";\n public const string ModelProvider = \"openai\";\n public const string ModelName = \"gpt-5-mini\";\n public const string TtsProvider = \"system\";\n\n public static string EffectivePlanner(string planner)\n {\n return string.IsNullOrWhiteSpace(planner) ? Planner : planner.Trim();\n }\n\n public static string EffectiveModelProvider(string provider)\n {\n return string.IsNullOrWhiteSpace(provider) ? ModelProvider : provider.Trim();\n }\n\n public static string EffectiveModelName(string modelName)\n {\n return string.IsNullOrWhiteSpace(modelName) ? ModelName : modelName.Trim();\n }\n\n public static string EffectiveTtsProvider(string provider)\n {\n return string.IsNullOrWhiteSpace(provider) ? TtsProvider : provider.Trim();\n }\n\n public static string ModelApiKeyName(string provider)\n {\n return EffectiveModelProvider(provider) switch\n {\n \"openai\" or \"openai-responses\" or \"openai-chat\" => \"OPENAI_API_KEY\",\n \"openrouter\" => \"OPENROUTER_API_KEY\",\n \"nous\" => \"NOUS_API_KEY\",\n \"novita\" => \"NOVITA_API_KEY\",\n \"lmstudio\" => \"LM_API_KEY\",\n \"anthropic\" => \"ANTHROPIC_API_KEY\",\n \"alibaba\" => \"DASHSCOPE_API_KEY\",\n \"groq\" => \"GROQ_API_KEY\",\n \"grok\" or \"xai\" => \"XAI_API_KEY\",\n \"gemini\" => \"GOOGLE_API_KEY\",\n \"deepseek\" => \"DEEPSEEK_API_KEY\",\n \"mistral\" => \"MISTRAL_API_KEY\",\n \"cerebras\" => \"CEREBRAS_API_KEY\",\n \"ollama\" => \"OLLAMA_API_KEY\",\n \"ollama-cloud\" => \"OLLAMA_API_KEY\",\n \"local-openai\" => \"LOCAL_LLM_API_KEY\",\n \"vercel\" => \"AI_GATEWAY_API_KEY\",\n \"litellm\" => \"LITELLM_API_KEY\",\n \"nvidia\" => \"NVIDIA_API_KEY\",\n \"huggingface\" => \"HF_TOKEN\",\n \"zai\" => \"GLM_API_KEY\",\n \"kimi-coding\" => \"KIMI_API_KEY\",\n \"kimi-coding-cn\" => \"KIMI_CN_API_KEY\",\n \"stepfun\" => \"STEPFUN_API_KEY\",\n \"minimax\" => \"MINIMAX_API_KEY\",\n \"minimax-cn\" => \"MINIMAX_CN_API_KEY\",\n \"arcee\" => \"ARCEEAI_API_KEY\",\n \"gmi\" => \"GMI_API_KEY\",\n \"xiaomi\" => \"XIAOMI_API_KEY\",\n \"tencent-tokenhub\" => \"TOKENHUB_API_KEY\",\n \"opencode-zen\" => \"OPENCODE_ZEN_API_KEY\",\n \"opencode-go\" => \"OPENCODE_GO_API_KEY\",\n \"kilocode\" => \"KILOCODE_API_KEY\",\n \"azure-openai\" => \"AZURE_OPENAI_API_KEY\",\n \"azure-foundry\" => \"AZURE_FOUNDRY_API_KEY\",\n \"copilot\" or \"copilot-acp\" => \"GITHUB_TOKEN\",\n \"bedrock\" => \"AWS_ACCESS_KEY_ID\",\n \"browser-use-cloud\" => \"BROWSER_USE_API_KEY\",\n _ => \"OPENAI_API_KEY\",\n };\n }\n\n public static string CliModelProvider(string provider)\n {\n return EffectiveModelProvider(provider) switch\n {\n \"openai\" => \"openai-responses\",\n var value => value,\n };\n }\n}\n"}, "b6cd6c9554b67cb4384a1185fa21589ad88b4c57": {"t": 1785756646.1755524, "n": 1557, "h": "c2b850d9c598f954", "c": "namespace Humungousaur.App.Models;\n\npublic static class AppRuntimeDefaults\n{\n public const string Planner = \"model\";\n public const string ModelProvider = \"openai\";\n public const string ModelName = \"gpt-5-mini\";\n public const string TtsProvider = \"system\";\n\n public static string EffectivePlanner(string planner)\n {\n return string.IsNullOrWhiteSpace(planner) ? Planner : planner.Trim();\n }\n\n public static string EffectiveModelProvider(string provider)\n {\n return string.IsNullOrWhiteSpace(provider) ? ModelProvider : provider.Trim();\n }\n\n public static string EffectiveModelName(string modelName)\n {\n return string.IsNullOrWhiteSpace(modelName) ? ModelName : modelName.Trim();\n }\n\n public static string EffectiveTtsProvider(string provider)\n {\n return string.IsNullOrWhiteSpace(provider) ? TtsProvider : provider.Trim();\n }\n\n public static string ModelApiKeyName(string provider)\n {\n return EffectiveModelProvider(provider) switch\n {\n \"openai\" or \"openai-responses\" or \"openai-chat\" => \"OPENAI_API_KEY\",\n \"groq\" => \"GROQ_API_KEY\",\n \"grok\" => \"XAI_API_KEY\",\n \"ollama\" => \"OLLAMA_API_KEY\",\n \"local-openai\" => \"LOCAL_LLM_API_KEY\",\n _ => \"OPENAI_API_KEY\",\n };\n }\n\n public static string CliModelProvider(string provider)\n {\n return EffectiveModelProvider(provider) switch\n {\n \"openai\" => \"openai-responses\",\n var value => value,\n };\n }\n}\n"}}, "apps/macos/Sources/LocalAgentProcess.swift": {"15d8b78fa52cafc8c0192e523f8a5eba43c7963b": {"t": 1785745885.7001894, "n": 7587, "h": "ef66593ab1522bf7", "c": "import Foundation\n\n@MainActor\nfinal class LocalAgentProcess: ObservableObject {\n @Published private(set) var isRunning = false\n @Published private(set) var logLines: [String] = []\n\n private var process: Process?\n private var stdoutPipe: Pipe?\n private var stderrPipe: Pipe?\n\n func start(settings: AppSettings, secrets: RuntimeSecrets) throws {\n guard process == nil || process?.isRunning == false else { return }\n\n let proc = Process()\n let pythonPath = settings.pythonPath.isEmpty ? \"python3\" : settings.pythonPath\n if pythonPath.contains(\"/\") {\n proc.executableURL = URL(fileURLWithPath: pythonPath)\n proc.arguments = []\n } else {\n proc.executableURL = URL(fileURLWithPath: \"/usr/bin/env\")\n proc.arguments = [pythonPath]\n }\n proc.currentDirectoryURL = URL(fileURLWithPath: settings.workspacePath)\n proc.arguments?.append(contentsOf: [\n \"-m\", \"humungousaur\",\n \"serve\",\n \"--workspace\", settings.workspacePath,\n \"--port\", String(settings.port),\n \"--planner\", settings.planner,\n \"--model-provider\", settings.modelProvider == \"openai\" ? \"openai-responses\" : settings.modelProvider,\n \"--model\", settings.modelName,\n \"--model-api-key-env\", modelKeyName(settings.modelProvider)\n ])\n if !settings.modelBaseURL.isEmpty {\n proc.arguments?.append(contentsOf: [\"--model-base-url\", settings.modelBaseURL])\n }\n let activeProvider = settings.janusModelProvider.trimmingCharacters(in: .whitespacesAndNewlines)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n proc.arguments?.append(contentsOf: [\n \"--janus-model-provider\", activeProvider == \"openai\" ? \"openai-responses\" : activeProvider,\n \"--janus-model-api-key-env\", modelKeyName(activeProvider)\n ])\n if !settings.janusModelName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n proc.arguments?.append(contentsOf: [\"--janus-model\", settings.janusModelName])\n }\n if !settings.janusModelBaseURL.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n proc.arguments?.append(contentsOf: [\"--janus-model-base-url\", settings.janusModelBaseURL])\n }\n }\n\n var environment = ProcessInfo.processInfo.environment\n addSecret(&environment, name: modelKeyName(settings.modelProvider), value: secrets.modelAPIKey)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n addSecret(&environment, name: modelKeyName(activeProvider), value: secrets.janusModelAPIKey)\n }\n addSecret(&environment, name: \"DEEPGRAM_API_KEY\", value: secrets.deepgramAPIKey)\n addSecret(&environment, name: \"ELEVENLABS_API_KEY\", value: secrets.elevenLabsAPIKey)\n addSecret(&environment, name: \"ELEVENLABS_VOICE_ID\", value: settings.voiceId)\n addSecret(&environment, name: \"ELEVENLABS_MODEL_ID\", value: settings.elevenLabsModel)\n for channel in settings.channels {\n addSecret(&environment, name: channel.secretName, value: channel.secretValue)\n for item in channel.secretValues {\n addSecret(&environment, name: item.key, value: item.value)\n }\n }\n addDefault(&environment, name: \"HUMUNGOUSAUR_CONNECTOR_OAUTH_BROKER_URL\", value: defaultConnectorOAuthBrokerURL)\n proc.environment = environment\n\n let output = Pipe()\n let error = Pipe()\n stdoutPipe = output\n stderrPipe = error\n proc.standardOutput = output\n proc.standardError = error\n\n output.fileHandleForReading.readabilityHandler = { [weak self] handle in\n let data = handle.availableData\n guard !data.isEmpty, let text = String(data: data, encoding: .utf8) else { return }\n Task { @MainActor in self?.appendLog(text) }\n }\n error.fileHandleForReading.readabilityHandler = { [weak self] handle in\n let data = handle.availableData\n guard !data.isEmpty, let text = String(data: data, encoding: .utf8) else { return }\n Task { @MainActor in self?.appendLog(text) }\n }\n proc.terminationHandler = { [weak self] _ in\n Task { @MainActor in\n self?.isRunning = false\n self?.appendLog(\"Agent process exited.\")\n }\n }\n\n try proc.run()\n process = proc\n isRunning = true\n appendLog(\"Started local Humungousaur daemon on port \\(settings.port).\")\n }\n\n func stop() {\n guard let process else { return }\n process.terminate()\n self.process = nil\n isRunning = false\n appendLog(\"Stop requested.\")\n }\n\n private func appendLog(_ text: String) {\n for line in text.components(separatedBy: .newlines).filter({ !$0.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).isEmpty }) {\n logLines.append(\"\\(Date.now.formatted(date: .omitted, time: .standard)) \\(line)\")\n }\n if logLines.count > 120 {\n logLines.removeFirst(logLines.count - 120)\n }\n }\n\n private func addSecret(_ environment: inout [String: String], name: String, value: String) {\n if !name.isEmpty, !value.isEmpty {\n environment[name] = value\n }\n }\n\n private func addDefault(_ environment: inout [String: String], name: String, value: String) {\n if environment[name]?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true {\n environment[name] = value\n }\n }\n\n private func modelKeyName(_ provider: String) -> String {\n switch provider {\n case \"openrouter\": \"OPENROUTER_API_KEY\"\n case \"nous\": \"NOUS_API_KEY\"\n case \"novita\": \"NOVITA_API_KEY\"\n case \"lmstudio\": \"LM_API_KEY\"\n case \"anthropic\": \"ANTHROPIC_API_KEY\"\n case \"alibaba\": \"DASHSCOPE_API_KEY\"\n case \"groq\": \"GROQ_API_KEY\"\n case \"grok\", \"xai\": \"XAI_API_KEY\"\n case \"gemini\": \"GOOGLE_API_KEY\"\n case \"deepseek\": \"DEEPSEEK_API_KEY\"\n case \"mistral\": \"MISTRAL_API_KEY\"\n case \"cerebras\": \"CEREBRAS_API_KEY\"\n case \"ollama\": \"OLLAMA_API_KEY\"\n case \"ollama-cloud\": \"OLLAMA_API_KEY\"\n case \"local-openai\": \"LOCAL_LLM_API_KEY\"\n case \"vercel\": \"AI_GATEWAY_API_KEY\"\n case \"litellm\": \"LITELLM_API_KEY\"\n case \"nvidia\": \"NVIDIA_API_KEY\"\n case \"huggingface\": \"HF_TOKEN\"\n case \"zai\": \"GLM_API_KEY\"\n case \"kimi-coding\": \"KIMI_API_KEY\"\n case \"kimi-coding-cn\": \"KIMI_CN_API_KEY\"\n case \"stepfun\": \"STEPFUN_API_KEY\"\n case \"minimax\": \"MINIMAX_API_KEY\"\n case \"minimax-cn\": \"MINIMAX_CN_API_KEY\"\n case \"arcee\": \"ARCEEAI_API_KEY\"\n case \"gmi\": \"GMI_API_KEY\"\n case \"xiaomi\": \"XIAOMI_API_KEY\"\n case \"tencent-tokenhub\": \"TOKENHUB_API_KEY\"\n case \"opencode-zen\": \"OPENCODE_ZEN_API_KEY\"\n case \"opencode-go\": \"OPENCODE_GO_API_KEY\"\n case \"kilocode\": \"KILOCODE_API_KEY\"\n case \"azure-openai\": \"AZURE_OPENAI_API_KEY\"\n case \"azure-foundry\": \"AZURE_FOUNDRY_API_KEY\"\n case \"copilot\", \"copilot-acp\": \"GITHUB_TOKEN\"\n case \"bedrock\": \"AWS_ACCESS_KEY_ID\"\n case \"browser-use-cloud\": \"BROWSER_USE_API_KEY\"\n default: \"OPENAI_API_KEY\"\n }\n }\n}\n\nprivate let defaultConnectorOAuthBrokerURL = \"https://humungousaur-connector-oauth.humungousaur.workers.dev\"\n"}, "10fede7ab8163961916ac9b545000527d92abb03": {"t": 1785756639.7739446, "n": 7587, "h": "ef66593ab1522bf7", "c": "import Foundation\n\n@MainActor\nfinal class LocalAgentProcess: ObservableObject {\n @Published private(set) var isRunning = false\n @Published private(set) var logLines: [String] = []\n\n private var process: Process?\n private var stdoutPipe: Pipe?\n private var stderrPipe: Pipe?\n\n func start(settings: AppSettings, secrets: RuntimeSecrets) throws {\n guard process == nil || process?.isRunning == false else { return }\n\n let proc = Process()\n let pythonPath = settings.pythonPath.isEmpty ? \"python3\" : settings.pythonPath\n if pythonPath.contains(\"/\") {\n proc.executableURL = URL(fileURLWithPath: pythonPath)\n proc.arguments = []\n } else {\n proc.executableURL = URL(fileURLWithPath: \"/usr/bin/env\")\n proc.arguments = [pythonPath]\n }\n proc.currentDirectoryURL = URL(fileURLWithPath: settings.workspacePath)\n proc.arguments?.append(contentsOf: [\n \"-m\", \"humungousaur\",\n \"serve\",\n \"--workspace\", settings.workspacePath,\n \"--port\", String(settings.port),\n \"--planner\", settings.planner,\n \"--model-provider\", settings.modelProvider == \"openai\" ? \"openai-responses\" : settings.modelProvider,\n \"--model\", settings.modelName,\n \"--model-api-key-env\", modelKeyName(settings.modelProvider)\n ])\n if !settings.modelBaseURL.isEmpty {\n proc.arguments?.append(contentsOf: [\"--model-base-url\", settings.modelBaseURL])\n }\n let activeProvider = settings.janusModelProvider.trimmingCharacters(in: .whitespacesAndNewlines)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n proc.arguments?.append(contentsOf: [\n \"--janus-model-provider\", activeProvider == \"openai\" ? \"openai-responses\" : activeProvider,\n \"--janus-model-api-key-env\", modelKeyName(activeProvider)\n ])\n if !settings.janusModelName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n proc.arguments?.append(contentsOf: [\"--janus-model\", settings.janusModelName])\n }\n if !settings.janusModelBaseURL.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n proc.arguments?.append(contentsOf: [\"--janus-model-base-url\", settings.janusModelBaseURL])\n }\n }\n\n var environment = ProcessInfo.processInfo.environment\n addSecret(&environment, name: modelKeyName(settings.modelProvider), value: secrets.modelAPIKey)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n addSecret(&environment, name: modelKeyName(activeProvider), value: secrets.janusModelAPIKey)\n }\n addSecret(&environment, name: \"DEEPGRAM_API_KEY\", value: secrets.deepgramAPIKey)\n addSecret(&environment, name: \"ELEVENLABS_API_KEY\", value: secrets.elevenLabsAPIKey)\n addSecret(&environment, name: \"ELEVENLABS_VOICE_ID\", value: settings.voiceId)\n addSecret(&environment, name: \"ELEVENLABS_MODEL_ID\", value: settings.elevenLabsModel)\n for channel in settings.channels {\n addSecret(&environment, name: channel.secretName, value: channel.secretValue)\n for item in channel.secretValues {\n addSecret(&environment, name: item.key, value: item.value)\n }\n }\n addDefault(&environment, name: \"HUMUNGOUSAUR_CONNECTOR_OAUTH_BROKER_URL\", value: defaultConnectorOAuthBrokerURL)\n proc.environment = environment\n\n let output = Pipe()\n let error = Pipe()\n stdoutPipe = output\n stderrPipe = error\n proc.standardOutput = output\n proc.standardError = error\n\n output.fileHandleForReading.readabilityHandler = { [weak self] handle in\n let data = handle.availableData\n guard !data.isEmpty, let text = String(data: data, encoding: .utf8) else { return }\n Task { @MainActor in self?.appendLog(text) }\n }\n error.fileHandleForReading.readabilityHandler = { [weak self] handle in\n let data = handle.availableData\n guard !data.isEmpty, let text = String(data: data, encoding: .utf8) else { return }\n Task { @MainActor in self?.appendLog(text) }\n }\n proc.terminationHandler = { [weak self] _ in\n Task { @MainActor in\n self?.isRunning = false\n self?.appendLog(\"Agent process exited.\")\n }\n }\n\n try proc.run()\n process = proc\n isRunning = true\n appendLog(\"Started local Humungousaur daemon on port \\(settings.port).\")\n }\n\n func stop() {\n guard let process else { return }\n process.terminate()\n self.process = nil\n isRunning = false\n appendLog(\"Stop requested.\")\n }\n\n private func appendLog(_ text: String) {\n for line in text.components(separatedBy: .newlines).filter({ !$0.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).isEmpty }) {\n logLines.append(\"\\(Date.now.formatted(date: .omitted, time: .standard)) \\(line)\")\n }\n if logLines.count > 120 {\n logLines.removeFirst(logLines.count - 120)\n }\n }\n\n private func addSecret(_ environment: inout [String: String], name: String, value: String) {\n if !name.isEmpty, !value.isEmpty {\n environment[name] = value\n }\n }\n\n private func addDefault(_ environment: inout [String: String], name: String, value: String) {\n if environment[name]?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true {\n environment[name] = value\n }\n }\n\n private func modelKeyName(_ provider: String) -> String {\n switch provider {\n case \"openrouter\": \"OPENROUTER_API_KEY\"\n case \"nous\": \"NOUS_API_KEY\"\n case \"novita\": \"NOVITA_API_KEY\"\n case \"lmstudio\": \"LM_API_KEY\"\n case \"anthropic\": \"ANTHROPIC_API_KEY\"\n case \"alibaba\": \"DASHSCOPE_API_KEY\"\n case \"groq\": \"GROQ_API_KEY\"\n case \"grok\", \"xai\": \"XAI_API_KEY\"\n case \"gemini\": \"GOOGLE_API_KEY\"\n case \"deepseek\": \"DEEPSEEK_API_KEY\"\n case \"mistral\": \"MISTRAL_API_KEY\"\n case \"cerebras\": \"CEREBRAS_API_KEY\"\n case \"ollama\": \"OLLAMA_API_KEY\"\n case \"ollama-cloud\": \"OLLAMA_API_KEY\"\n case \"local-openai\": \"LOCAL_LLM_API_KEY\"\n case \"vercel\": \"AI_GATEWAY_API_KEY\"\n case \"litellm\": \"LITELLM_API_KEY\"\n case \"nvidia\": \"NVIDIA_API_KEY\"\n case \"huggingface\": \"HF_TOKEN\"\n case \"zai\": \"GLM_API_KEY\"\n case \"kimi-coding\": \"KIMI_API_KEY\"\n case \"kimi-coding-cn\": \"KIMI_CN_API_KEY\"\n case \"stepfun\": \"STEPFUN_API_KEY\"\n case \"minimax\": \"MINIMAX_API_KEY\"\n case \"minimax-cn\": \"MINIMAX_CN_API_KEY\"\n case \"arcee\": \"ARCEEAI_API_KEY\"\n case \"gmi\": \"GMI_API_KEY\"\n case \"xiaomi\": \"XIAOMI_API_KEY\"\n case \"tencent-tokenhub\": \"TOKENHUB_API_KEY\"\n case \"opencode-zen\": \"OPENCODE_ZEN_API_KEY\"\n case \"opencode-go\": \"OPENCODE_GO_API_KEY\"\n case \"kilocode\": \"KILOCODE_API_KEY\"\n case \"azure-openai\": \"AZURE_OPENAI_API_KEY\"\n case \"azure-foundry\": \"AZURE_FOUNDRY_API_KEY\"\n case \"copilot\", \"copilot-acp\": \"GITHUB_TOKEN\"\n case \"bedrock\": \"AWS_ACCESS_KEY_ID\"\n case \"browser-use-cloud\": \"BROWSER_USE_API_KEY\"\n default: \"OPENAI_API_KEY\"\n }\n }\n}\n\nprivate let defaultConnectorOAuthBrokerURL = \"https://humungousaur-connector-oauth.humungousaur.workers.dev\"\n"}, "5272e1d0a3d25da8abf9dfe0b0dfbf99779f9c00": {"t": 1785756641.0671492, "n": 7107, "h": "f07d555e836b6f0f", "c": "import Foundation\n\n@MainActor\nfinal class LocalAgentProcess: ObservableObject {\n @Published private(set) var isRunning = false\n @Published private(set) var logLines: [String] = []\n\n private var process: Process?\n private var stdoutPipe: Pipe?\n private var stderrPipe: Pipe?\n\n func start(settings: AppSettings, secrets: RuntimeSecrets) throws {\n guard process == nil || process?.isRunning == false else { return }\n\n let proc = Process()\n let pythonPath = settings.pythonPath.isEmpty ? \"python3\" : settings.pythonPath\n if pythonPath.contains(\"/\") {\n proc.executableURL = URL(fileURLWithPath: pythonPath)\n proc.arguments = []\n } else {\n proc.executableURL = URL(fileURLWithPath: \"/usr/bin/env\")\n proc.arguments = [pythonPath]\n }\n proc.currentDirectoryURL = URL(fileURLWithPath: settings.workspacePath)\n proc.arguments?.append(contentsOf: [\n \"-m\", \"humungousaur\",\n \"serve\",\n \"--workspace\", settings.workspacePath,\n \"--port\", String(settings.port),\n \"--planner\", settings.planner,\n \"--model-provider\", settings.modelProvider == \"openai\" ? \"openai-responses\" : settings.modelProvider,\n \"--model\", settings.modelName,\n \"--model-api-key-env\", modelKeyName(settings.modelProvider)\n ])\n if !settings.modelBaseURL.isEmpty {\n proc.arguments?.append(contentsOf: [\"--model-base-url\", settings.modelBaseURL])\n }\n let activeProvider = settings.janusModelProvider.trimmingCharacters(in: .whitespacesAndNewlines)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n proc.arguments?.append(contentsOf: [\n \"--janus-model-provider\", activeProvider == \"openai\" ? \"openai-responses\" : activeProvider,\n \"--janus-model-api-key-env\", modelKeyName(activeProvider)\n ])\n if !settings.janusModelName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n proc.arguments?.append(contentsOf: [\"--janus-model\", settings.janusModelName])\n }\n if !settings.janusModelBaseURL.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n proc.arguments?.append(contentsOf: [\"--janus-model-base-url\", settings.janusModelBaseURL])\n }\n }\n\n var environment = ProcessInfo.processInfo.environment\n addSecret(&environment, name: modelKeyName(settings.modelProvider), value: secrets.modelAPIKey)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n addSecret(&environment, name: modelKeyName(activeProvider), value: secrets.janusModelAPIKey)\n }\n addSecret(&environment, name: \"DEEPGRAM_API_KEY\", value: secrets.deepgramAPIKey)\n addSecret(&environment, name: \"ELEVENLABS_API_KEY\", value: secrets.elevenLabsAPIKey)\n addSecret(&environment, name: \"ELEVENLABS_VOICE_ID\", value: settings.voiceId)\n addSecret(&environment, name: \"ELEVENLABS_MODEL_ID\", value: settings.elevenLabsModel)\n for channel in settings.channels {\n addSecret(&environment, name: channel.secretName, value: channel.secretValue)\n for item in channel.secretValues {\n addSecret(&environment, name: item.key, value: item.value)\n }\n }\n proc.environment = environment\n\n let output = Pipe()\n let error = Pipe()\n stdoutPipe = output\n stderrPipe = error\n proc.standardOutput = output\n proc.standardError = error\n\n output.fileHandleForReading.readabilityHandler = { [weak self] handle in\n let data = handle.availableData\n guard !data.isEmpty, let text = String(data: data, encoding: .utf8) else { return }\n Task { @MainActor in self?.appendLog(text) }\n }\n error.fileHandleForReading.readabilityHandler = { [weak self] handle in\n let data = handle.availableData\n guard !data.isEmpty, let text = String(data: data, encoding: .utf8) else { return }\n Task { @MainActor in self?.appendLog(text) }\n }\n proc.terminationHandler = { [weak self] _ in\n Task { @MainActor in\n self?.isRunning = false\n self?.appendLog(\"Agent process exited.\")\n }\n }\n\n try proc.run()\n process = proc\n isRunning = true\n appendLog(\"Started local Humungousaur daemon on port \\(settings.port).\")\n }\n\n func stop() {\n guard let process else { return }\n process.terminate()\n self.process = nil\n isRunning = false\n appendLog(\"Stop requested.\")\n }\n\n private func appendLog(_ text: String) {\n for line in text.components(separatedBy: .newlines).filter({ !$0.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).isEmpty }) {\n logLines.append(\"\\(Date.now.formatted(date: .omitted, time: .standard)) \\(line)\")\n }\n if logLines.count > 120 {\n logLines.removeFirst(logLines.count - 120)\n }\n }\n\n private func addSecret(_ environment: inout [String: String], name: String, value: String) {\n if !name.isEmpty, !value.isEmpty {\n environment[name] = value\n }\n }\n\n private func modelKeyName(_ provider: String) -> String {\n switch provider {\n case \"openrouter\": \"OPENROUTER_API_KEY\"\n case \"nous\": \"NOUS_API_KEY\"\n case \"novita\": \"NOVITA_API_KEY\"\n case \"lmstudio\": \"LM_API_KEY\"\n case \"anthropic\": \"ANTHROPIC_API_KEY\"\n case \"alibaba\": \"DASHSCOPE_API_KEY\"\n case \"groq\": \"GROQ_API_KEY\"\n case \"grok\", \"xai\": \"XAI_API_KEY\"\n case \"gemini\": \"GOOGLE_API_KEY\"\n case \"deepseek\": \"DEEPSEEK_API_KEY\"\n case \"mistral\": \"MISTRAL_API_KEY\"\n case \"cerebras\": \"CEREBRAS_API_KEY\"\n case \"ollama\": \"OLLAMA_API_KEY\"\n case \"ollama-cloud\": \"OLLAMA_API_KEY\"\n case \"local-openai\": \"LOCAL_LLM_API_KEY\"\n case \"vercel\": \"AI_GATEWAY_API_KEY\"\n case \"litellm\": \"LITELLM_API_KEY\"\n case \"nvidia\": \"NVIDIA_API_KEY\"\n case \"huggingface\": \"HF_TOKEN\"\n case \"zai\": \"GLM_API_KEY\"\n case \"kimi-coding\": \"KIMI_API_KEY\"\n case \"kimi-coding-cn\": \"KIMI_CN_API_KEY\"\n case \"stepfun\": \"STEPFUN_API_KEY\"\n case \"minimax\": \"MINIMAX_API_KEY\"\n case \"minimax-cn\": \"MINIMAX_CN_API_KEY\"\n case \"arcee\": \"ARCEEAI_API_KEY\"\n case \"gmi\": \"GMI_API_KEY\"\n case \"xiaomi\": \"XIAOMI_API_KEY\"\n case \"tencent-tokenhub\": \"TOKENHUB_API_KEY\"\n case \"opencode-zen\": \"OPENCODE_ZEN_API_KEY\"\n case \"opencode-go\": \"OPENCODE_GO_API_KEY\"\n case \"kilocode\": \"KILOCODE_API_KEY\"\n case \"azure-openai\": \"AZURE_OPENAI_API_KEY\"\n case \"azure-foundry\": \"AZURE_FOUNDRY_API_KEY\"\n case \"copilot\", \"copilot-acp\": \"GITHUB_TOKEN\"\n case \"bedrock\": \"AWS_ACCESS_KEY_ID\"\n case \"browser-use-cloud\": \"BROWSER_USE_API_KEY\"\n default: \"OPENAI_API_KEY\"\n }\n }\n}\n"}, "8d082d4caf3cfac1a7f0f329ec876366f53df68c": {"t": 1785756642.3513634, "n": 7117, "h": "baa3c1ae71e05f71", "c": "import Foundation\n\n@MainActor\nfinal class LocalAgentProcess: ObservableObject {\n @Published private(set) var isRunning = false\n @Published private(set) var logLines: [String] = []\n\n private var process: Process?\n private var stdoutPipe: Pipe?\n private var stderrPipe: Pipe?\n\n func start(settings: AppSettings, secrets: RuntimeSecrets) throws {\n guard process == nil || process?.isRunning == false else { return }\n\n let proc = Process()\n let pythonPath = settings.pythonPath.isEmpty ? \"python3\" : settings.pythonPath\n if pythonPath.contains(\"/\") {\n proc.executableURL = URL(fileURLWithPath: pythonPath)\n proc.arguments = []\n } else {\n proc.executableURL = URL(fileURLWithPath: \"/usr/bin/env\")\n proc.arguments = [pythonPath]\n }\n proc.currentDirectoryURL = URL(fileURLWithPath: settings.workspacePath)\n proc.arguments?.append(contentsOf: [\n \"-m\", \"humungousaur\",\n \"serve\",\n \"--workspace\", settings.workspacePath,\n \"--port\", String(settings.port),\n \"--planner\", settings.planner,\n \"--model-provider\", settings.modelProvider == \"openai\" ? \"openai-responses\" : settings.modelProvider,\n \"--model\", settings.modelName,\n \"--model-api-key-env\", modelKeyName(settings.modelProvider)\n ])\n if !settings.modelBaseURL.isEmpty {\n proc.arguments?.append(contentsOf: [\"--model-base-url\", settings.modelBaseURL])\n }\n let activeProvider = settings.activeModelProvider.trimmingCharacters(in: .whitespacesAndNewlines)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n proc.arguments?.append(contentsOf: [\n \"--active-model-provider\", activeProvider == \"openai\" ? \"openai-responses\" : activeProvider,\n \"--active-model-api-key-env\", modelKeyName(activeProvider)\n ])\n if !settings.activeModelName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n proc.arguments?.append(contentsOf: [\"--active-model\", settings.activeModelName])\n }\n if !settings.activeModelBaseURL.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n proc.arguments?.append(contentsOf: [\"--active-model-base-url\", settings.activeModelBaseURL])\n }\n }\n\n var environment = ProcessInfo.processInfo.environment\n addSecret(&environment, name: modelKeyName(settings.modelProvider), value: secrets.modelAPIKey)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n addSecret(&environment, name: modelKeyName(activeProvider), value: secrets.activeModelAPIKey)\n }\n addSecret(&environment, name: \"DEEPGRAM_API_KEY\", value: secrets.deepgramAPIKey)\n addSecret(&environment, name: \"ELEVENLABS_API_KEY\", value: secrets.elevenLabsAPIKey)\n addSecret(&environment, name: \"ELEVENLABS_VOICE_ID\", value: settings.voiceId)\n addSecret(&environment, name: \"ELEVENLABS_MODEL_ID\", value: settings.elevenLabsModel)\n for channel in settings.channels {\n addSecret(&environment, name: channel.secretName, value: channel.secretValue)\n for item in channel.secretValues {\n addSecret(&environment, name: item.key, value: item.value)\n }\n }\n proc.environment = environment\n\n let output = Pipe()\n let error = Pipe()\n stdoutPipe = output\n stderrPipe = error\n proc.standardOutput = output\n proc.standardError = error\n\n output.fileHandleForReading.readabilityHandler = { [weak self] handle in\n let data = handle.availableData\n guard !data.isEmpty, let text = String(data: data, encoding: .utf8) else { return }\n Task { @MainActor in self?.appendLog(text) }\n }\n error.fileHandleForReading.readabilityHandler = { [weak self] handle in\n let data = handle.availableData\n guard !data.isEmpty, let text = String(data: data, encoding: .utf8) else { return }\n Task { @MainActor in self?.appendLog(text) }\n }\n proc.terminationHandler = { [weak self] _ in\n Task { @MainActor in\n self?.isRunning = false\n self?.appendLog(\"Agent process exited.\")\n }\n }\n\n try proc.run()\n process = proc\n isRunning = true\n appendLog(\"Started local Humungousaur daemon on port \\(settings.port).\")\n }\n\n func stop() {\n guard let process else { return }\n process.terminate()\n self.process = nil\n isRunning = false\n appendLog(\"Stop requested.\")\n }\n\n private func appendLog(_ text: String) {\n for line in text.components(separatedBy: .newlines).filter({ !$0.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).isEmpty }) {\n logLines.append(\"\\(Date.now.formatted(date: .omitted, time: .standard)) \\(line)\")\n }\n if logLines.count > 120 {\n logLines.removeFirst(logLines.count - 120)\n }\n }\n\n private func addSecret(_ environment: inout [String: String], name: String, value: String) {\n if !name.isEmpty, !value.isEmpty {\n environment[name] = value\n }\n }\n\n private func modelKeyName(_ provider: String) -> String {\n switch provider {\n case \"openrouter\": \"OPENROUTER_API_KEY\"\n case \"nous\": \"NOUS_API_KEY\"\n case \"novita\": \"NOVITA_API_KEY\"\n case \"lmstudio\": \"LM_API_KEY\"\n case \"anthropic\": \"ANTHROPIC_API_KEY\"\n case \"alibaba\": \"DASHSCOPE_API_KEY\"\n case \"groq\": \"GROQ_API_KEY\"\n case \"grok\", \"xai\": \"XAI_API_KEY\"\n case \"gemini\": \"GOOGLE_API_KEY\"\n case \"deepseek\": \"DEEPSEEK_API_KEY\"\n case \"mistral\": \"MISTRAL_API_KEY\"\n case \"cerebras\": \"CEREBRAS_API_KEY\"\n case \"ollama\": \"OLLAMA_API_KEY\"\n case \"ollama-cloud\": \"OLLAMA_API_KEY\"\n case \"local-openai\": \"LOCAL_LLM_API_KEY\"\n case \"vercel\": \"AI_GATEWAY_API_KEY\"\n case \"litellm\": \"LITELLM_API_KEY\"\n case \"nvidia\": \"NVIDIA_API_KEY\"\n case \"huggingface\": \"HF_TOKEN\"\n case \"zai\": \"GLM_API_KEY\"\n case \"kimi-coding\": \"KIMI_API_KEY\"\n case \"kimi-coding-cn\": \"KIMI_CN_API_KEY\"\n case \"stepfun\": \"STEPFUN_API_KEY\"\n case \"minimax\": \"MINIMAX_API_KEY\"\n case \"minimax-cn\": \"MINIMAX_CN_API_KEY\"\n case \"arcee\": \"ARCEEAI_API_KEY\"\n case \"gmi\": \"GMI_API_KEY\"\n case \"xiaomi\": \"XIAOMI_API_KEY\"\n case \"tencent-tokenhub\": \"TOKENHUB_API_KEY\"\n case \"opencode-zen\": \"OPENCODE_ZEN_API_KEY\"\n case \"opencode-go\": \"OPENCODE_GO_API_KEY\"\n case \"kilocode\": \"KILOCODE_API_KEY\"\n case \"azure-openai\": \"AZURE_OPENAI_API_KEY\"\n case \"azure-foundry\": \"AZURE_FOUNDRY_API_KEY\"\n case \"copilot\", \"copilot-acp\": \"GITHUB_TOKEN\"\n case \"bedrock\": \"AWS_ACCESS_KEY_ID\"\n case \"browser-use-cloud\": \"BROWSER_USE_API_KEY\"\n default: \"OPENAI_API_KEY\"\n }\n }\n}\n"}, "0f91a0d2387b42a7fd6f41a54687733d878010f6": {"t": 1785756644.7800615, "n": 6060, "h": "5c45ee6e552580d4", "c": "import Foundation\n\n@MainActor\nfinal class LocalAgentProcess: ObservableObject {\n @Published private(set) var isRunning = false\n @Published private(set) var logLines: [String] = []\n\n private var process: Process?\n private var stdoutPipe: Pipe?\n private var stderrPipe: Pipe?\n\n func start(settings: AppSettings, secrets: RuntimeSecrets) throws {\n guard process == nil || process?.isRunning == false else { return }\n\n let proc = Process()\n let pythonPath = settings.pythonPath.isEmpty ? \"python3\" : settings.pythonPath\n if pythonPath.contains(\"/\") {\n proc.executableURL = URL(fileURLWithPath: pythonPath)\n proc.arguments = []\n } else {\n proc.executableURL = URL(fileURLWithPath: \"/usr/bin/env\")\n proc.arguments = [pythonPath]\n }\n proc.currentDirectoryURL = URL(fileURLWithPath: settings.workspacePath)\n proc.arguments?.append(contentsOf: [\n \"-m\", \"humungousaur\",\n \"serve\",\n \"--workspace\", settings.workspacePath,\n \"--port\", String(settings.port),\n \"--planner\", settings.planner,\n \"--model-provider\", settings.modelProvider == \"openai\" ? \"openai-responses\" : settings.modelProvider,\n \"--model\", settings.modelName,\n \"--model-api-key-env\", modelKeyName(settings.modelProvider)\n ])\n if !settings.modelBaseURL.isEmpty {\n proc.arguments?.append(contentsOf: [\"--model-base-url\", settings.modelBaseURL])\n }\n\n var environment = ProcessInfo.processInfo.environment\n addSecret(&environment, name: modelKeyName(settings.modelProvider), value: secrets.modelAPIKey)\n addSecret(&environment, name: \"DEEPGRAM_API_KEY\", value: secrets.deepgramAPIKey)\n addSecret(&environment, name: \"ELEVENLABS_API_KEY\", value: secrets.elevenLabsAPIKey)\n addSecret(&environment, name: \"ELEVENLABS_VOICE_ID\", value: settings.voiceId)\n addSecret(&environment, name: \"ELEVENLABS_MODEL_ID\", value: settings.elevenLabsModel)\n for channel in settings.channels {\n addSecret(&environment, name: channel.secretName, value: channel.secretValue)\n for item in channel.secretValues {\n addSecret(&environment, name: item.key, value: item.value)\n }\n }\n proc.environment = environment\n\n let output = Pipe()\n let error = Pipe()\n stdoutPipe = output\n stderrPipe = error\n proc.standardOutput = output\n proc.standardError = error\n\n output.fileHandleForReading.readabilityHandler = { [weak self] handle in\n let data = handle.availableData\n guard !data.isEmpty, let text = String(data: data, encoding: .utf8) else { return }\n Task { @MainActor in self?.appendLog(text) }\n }\n error.fileHandleForReading.readabilityHandler = { [weak self] handle in\n let data = handle.availableData\n guard !data.isEmpty, let text = String(data: data, encoding: .utf8) else { return }\n Task { @MainActor in self?.appendLog(text) }\n }\n proc.terminationHandler = { [weak self] _ in\n Task { @MainActor in\n self?.isRunning = false\n self?.appendLog(\"Agent process exited.\")\n }\n }\n\n try proc.run()\n process = proc\n isRunning = true\n appendLog(\"Started local Humungousaur daemon on port \\(settings.port).\")\n }\n\n func stop() {\n guard let process else { return }\n process.terminate()\n self.process = nil\n isRunning = false\n appendLog(\"Stop requested.\")\n }\n\n private func appendLog(_ text: String) {\n for line in text.components(separatedBy: .newlines).filter({ !$0.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).isEmpty }) {\n logLines.append(\"\\(Date.now.formatted(date: .omitted, time: .standard)) \\(line)\")\n }\n if logLines.count > 120 {\n logLines.removeFirst(logLines.count - 120)\n }\n }\n\n private func addSecret(_ environment: inout [String: String], name: String, value: String) {\n if !name.isEmpty, !value.isEmpty {\n environment[name] = value\n }\n }\n\n private func modelKeyName(_ provider: String) -> String {\n switch provider {\n case \"openrouter\": \"OPENROUTER_API_KEY\"\n case \"nous\": \"NOUS_API_KEY\"\n case \"novita\": \"NOVITA_API_KEY\"\n case \"lmstudio\": \"LM_API_KEY\"\n case \"anthropic\": \"ANTHROPIC_API_KEY\"\n case \"alibaba\": \"DASHSCOPE_API_KEY\"\n case \"groq\": \"GROQ_API_KEY\"\n case \"grok\", \"xai\": \"XAI_API_KEY\"\n case \"gemini\": \"GOOGLE_API_KEY\"\n case \"deepseek\": \"DEEPSEEK_API_KEY\"\n case \"mistral\": \"MISTRAL_API_KEY\"\n case \"cerebras\": \"CEREBRAS_API_KEY\"\n case \"ollama\": \"OLLAMA_API_KEY\"\n case \"ollama-cloud\": \"OLLAMA_API_KEY\"\n case \"local-openai\": \"LOCAL_LLM_API_KEY\"\n case \"vercel\": \"AI_GATEWAY_API_KEY\"\n case \"litellm\": \"LITELLM_API_KEY\"\n case \"nvidia\": \"NVIDIA_API_KEY\"\n case \"huggingface\": \"HF_TOKEN\"\n case \"zai\": \"GLM_API_KEY\"\n case \"kimi-coding\": \"KIMI_API_KEY\"\n case \"kimi-coding-cn\": \"KIMI_CN_API_KEY\"\n case \"stepfun\": \"STEPFUN_API_KEY\"\n case \"minimax\": \"MINIMAX_API_KEY\"\n case \"minimax-cn\": \"MINIMAX_CN_API_KEY\"\n case \"arcee\": \"ARCEEAI_API_KEY\"\n case \"gmi\": \"GMI_API_KEY\"\n case \"xiaomi\": \"XIAOMI_API_KEY\"\n case \"tencent-tokenhub\": \"TOKENHUB_API_KEY\"\n case \"opencode-zen\": \"OPENCODE_ZEN_API_KEY\"\n case \"opencode-go\": \"OPENCODE_GO_API_KEY\"\n case \"kilocode\": \"KILOCODE_API_KEY\"\n case \"azure-openai\": \"AZURE_OPENAI_API_KEY\"\n case \"azure-foundry\": \"AZURE_FOUNDRY_API_KEY\"\n case \"copilot\", \"copilot-acp\": \"GITHUB_TOKEN\"\n case \"bedrock\": \"AWS_ACCESS_KEY_ID\"\n case \"browser-use-cloud\": \"BROWSER_USE_API_KEY\"\n default: \"OPENAI_API_KEY\"\n }\n }\n}\n"}, "51ad05013df285208a4bb212406d76ae6127a179": {"t": 1785756645.977462, "n": 4590, "h": "897524ea2d1ab924", "c": "import Foundation\n\n@MainActor\nfinal class LocalAgentProcess: ObservableObject {\n @Published private(set) var isRunning = false\n @Published private(set) var logLines: [String] = []\n\n private var process: Process?\n private var stdoutPipe: Pipe?\n private var stderrPipe: Pipe?\n\n func start(settings: AppSettings, secrets: RuntimeSecrets) throws {\n guard process == nil || process?.isRunning == false else { return }\n\n let proc = Process()\n let pythonPath = settings.pythonPath.isEmpty ? \"python3\" : settings.pythonPath\n if pythonPath.contains(\"/\") {\n proc.executableURL = URL(fileURLWithPath: pythonPath)\n proc.arguments = []\n } else {\n proc.executableURL = URL(fileURLWithPath: \"/usr/bin/env\")\n proc.arguments = [pythonPath]\n }\n proc.currentDirectoryURL = URL(fileURLWithPath: settings.workspacePath)\n proc.arguments?.append(contentsOf: [\n \"-m\", \"humungousaur\",\n \"serve\",\n \"--workspace\", settings.workspacePath,\n \"--port\", String(settings.port),\n \"--planner\", settings.planner,\n \"--model-provider\", settings.modelProvider == \"openai\" ? \"openai-responses\" : settings.modelProvider,\n \"--model\", settings.modelName,\n \"--model-api-key-env\", modelKeyName(settings.modelProvider)\n ])\n if !settings.modelBaseURL.isEmpty {\n proc.arguments?.append(contentsOf: [\"--model-base-url\", settings.modelBaseURL])\n }\n\n var environment = ProcessInfo.processInfo.environment\n addSecret(&environment, name: modelKeyName(settings.modelProvider), value: secrets.modelAPIKey)\n addSecret(&environment, name: \"DEEPGRAM_API_KEY\", value: secrets.deepgramAPIKey)\n addSecret(&environment, name: \"ELEVENLABS_API_KEY\", value: secrets.elevenLabsAPIKey)\n addSecret(&environment, name: \"ELEVENLABS_VOICE_ID\", value: settings.voiceId)\n addSecret(&environment, name: \"ELEVENLABS_MODEL_ID\", value: settings.elevenLabsModel)\n for channel in settings.channels {\n addSecret(&environment, name: channel.secretName, value: channel.secretValue)\n for item in channel.secretValues {\n addSecret(&environment, name: item.key, value: item.value)\n }\n }\n proc.environment = environment\n\n let output = Pipe()\n let error = Pipe()\n stdoutPipe = output\n stderrPipe = error\n proc.standardOutput = output\n proc.standardError = error\n\n output.fileHandleForReading.readabilityHandler = { [weak self] handle in\n let data = handle.availableData\n guard !data.isEmpty, let text = String(data: data, encoding: .utf8) else { return }\n Task { @MainActor in self?.appendLog(text) }\n }\n error.fileHandleForReading.readabilityHandler = { [weak self] handle in\n let data = handle.availableData\n guard !data.isEmpty, let text = String(data: data, encoding: .utf8) else { return }\n Task { @MainActor in self?.appendLog(text) }\n }\n proc.terminationHandler = { [weak self] _ in\n Task { @MainActor in\n self?.isRunning = false\n self?.appendLog(\"Agent process exited.\")\n }\n }\n\n try proc.run()\n process = proc\n isRunning = true\n appendLog(\"Started local Humungousaur daemon on port \\(settings.port).\")\n }\n\n func stop() {\n guard let process else { return }\n process.terminate()\n self.process = nil\n isRunning = false\n appendLog(\"Stop requested.\")\n }\n\n private func appendLog(_ text: String) {\n for line in text.components(separatedBy: .newlines).filter({ !$0.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).isEmpty }) {\n logLines.append(\"\\(Date.now.formatted(date: .omitted, time: .standard)) \\(line)\")\n }\n if logLines.count > 120 {\n logLines.removeFirst(logLines.count - 120)\n }\n }\n\n private func addSecret(_ environment: inout [String: String], name: String, value: String) {\n if !name.isEmpty, !value.isEmpty {\n environment[name] = value\n }\n }\n\n private func modelKeyName(_ provider: String) -> String {\n switch provider {\n case \"groq\": \"GROQ_API_KEY\"\n case \"grok\": \"XAI_API_KEY\"\n case \"ollama\": \"OLLAMA_API_KEY\"\n case \"local-openai\": \"LOCAL_LLM_API_KEY\"\n default: \"OPENAI_API_KEY\"\n }\n }\n}\n"}}, "apps/macos/Sources/AgentAPIClient.swift": {"15d8b78fa52cafc8c0192e523f8a5eba43c7963b": {"t": 1785745887.3620048, "n": 31808, "h": "88fa482ea90c6e5b", "c": "import Foundation\n\nstruct RuntimeSecrets {\n var modelAPIKey = \"\"\n var janusModelAPIKey = \"\"\n var deepgramAPIKey = \"\"\n var elevenLabsAPIKey = \"\"\n}\n\n@MainActor\nfinal class AgentAPIClient {\n private var baseURL: URL\n private let decoder: JSONDecoder\n private let encoder = JSONEncoder()\n private let responseFormattingInstruction = \"\"\"\n Respond with a detailed final answer in GitHub-flavored Markdown. Use clear sections, bullets, and tables where they make the answer easier to scan. Include caveats, source/tool evidence, and next steps when relevant. Do not return a single unformatted paragraph unless the user explicitly asks for a very short answer.\n \"\"\"\n private let voiceResponseFormattingInstruction = \"\"\"\n The user is speaking by voice. Reply warmly and conversationally in a voice-friendly style: lead with the direct answer, keep sentences short, use short paragraphs, avoid tables unless essential, and use bullets only when they make the spoken answer clearer. Keep the final answer easy to read aloud.\n \"\"\"\n\n init(baseURL: String) {\n self.baseURL = AgentAPIClient.normalizedBaseURL(baseURL)\n decoder = JSONDecoder()\n }\n\n func setBaseURL(_ value: String) {\n baseURL = AgentAPIClient.normalizedBaseURL(value, fallback: baseURL)\n }\n\n func health() async throws -> HealthPayload {\n try await get(\"health\")\n }\n\n func systemStatus() async throws -> JSONValue {\n try await get(\"system/status\")\n }\n\n func latestUpdate() async throws -> UpdateInfo {\n try await get(\"updates/latest?platform=macos\")\n }\n\n func tools() async throws -> ToolCatalog {\n try await get(\"tools\")\n }\n\n func modelProviders() async throws -> ModelProviderCatalog {\n try await get(\"model/providers\")\n }\n\n func runs() async throws -> [RunItem] {\n try await get(\"runs?limit=30\")\n }\n\n func chatConversations() async throws -> [ChatConversation] {\n let envelope: ChatConversationEnvelope = try await get(\"chats?limit=80\")\n return envelope.conversations\n }\n\n func createChat(title: String = \"New chat\", source: String = \"macos_app\") async throws -> ChatConversation {\n let response: ChatConversationResponse = try await post(\n \"chats\",\n body: [\n \"title\": title,\n \"source\": source,\n \"metadata\": [\"client\": \"macos\"]\n ]\n )\n return response.conversation\n }\n\n func chatMessages(conversationID: String) async throws -> ChatMessagesEnvelope {\n try await get(\"chats/\\(Self.urlEncode(conversationID))/messages?limit=300\")\n }\n\n func startChatRun(conversationID: String, text: String, displayText: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> ChatRunQueuedResponse {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n let voiceFriendly = isVoiceRequest(source: source, responseMode: responseMode)\n payload[\"request\"] = formattedRequestText(text, source: source, responseMode: responseMode)\n payload[\"display_text\"] = displayText\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": voiceFriendly ? \"voice_friendly\" : \"detailed\",\n \"response_style\": voiceFriendly ? \"spoken_conversation\" : \"written_markdown\",\n \"client\": \"macos\"\n ]\n return try await post(\"chats/\\(Self.urlEncode(conversationID))/runs/async\", body: payload)\n }\n\n func approvals() async throws -> [ApprovalItem] {\n try await get(\"approvals?status=pending&limit=30\")\n }\n\n func channels() async throws -> [ChannelInfo] {\n try await get(\"channels\")\n }\n\n func connectors() async throws -> ConnectorCatalog {\n try await get(\"connectors\")\n }\n\n func configureConnector(providerID: String, credentials: [String: String], redirectURI: String) async throws -> JSONValue {\n try await post(\n \"connectors/configure\",\n body: [\n \"provider_id\": providerID,\n \"credentials\": credentials,\n \"redirect_uri\": redirectURI\n ]\n )\n }\n\n func prepareConnector(providerID: String) async throws -> ConnectorAuthorization {\n try await post(\"connectors/connect\", body: [\"provider_id\": providerID])\n }\n\n func refreshConnector(providerID: String) async throws -> JSONValue {\n try await post(\"connectors/refresh\", body: [\"provider_id\": providerID])\n }\n\n func disconnectConnector(providerID: String) async throws -> JSONValue {\n try await post(\"connectors/disconnect\", body: [\"provider_id\": providerID])\n }\n\n func outbox() async throws -> OutboxEnvelope {\n try await get(\"channels/outbox\")\n }\n\n func channelStatus(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/status\", body: payload)\n }\n\n func channelDoctor(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/doctor\", body: payload)\n }\n\n func channelRequirements(channelID: String) async throws -> JSONValue {\n try await get(\"channels/requirements?channel_id=\\(Self.urlEncode(channelID))\")\n }\n\n func runChannelSmoke(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"prepare_messages\"] = true\n payload[\"dry_run_sends\"] = true\n return try await post(\"channels/smoke\", body: payload)\n }\n\n func channelListeners(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/listeners\", body: payload)\n }\n\n func tickChannelListener(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func tickAllChannelListeners(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func saveChannelSetup(channel: ChannelInfo, setup: ChannelSetup) async throws -> JSONValue {\n let payload: [String: Any] = [\n \"channel_id\": channel.channelId,\n \"enabled\": setup.enabled,\n \"listen_enabled\": setup.listenEnabled,\n \"conversation_defaults\": [\n \"conversation_id\": setup.conversationId,\n \"conversation_type\": setup.conversationType\n ],\n \"secret_refs\": channelSecretRefs(setup),\n \"secret_configured\": channelSecretConfigured(setup),\n \"allowlist\": cleanList(setup.allowlist),\n \"group_allowlist\": cleanList(setup.groupAllowlist),\n \"notes\": setup.notes\n ]\n return try await post(\"channels/setup\", body: payload)\n }\n\n func prepareChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Prepared from the macOS app channel panel.\"\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/prepare\", body: payload)\n }\n\n func sendChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Approval-gated send from the macOS app channel panel.\"\n payload[\"approve_high_risk\"] = settings.approveHighRisk\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/send\", body: payload)\n }\n\n func sendChannelInbound(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"conversation_type\"] = setup.conversationType.isEmpty ? \"dm\" : setup.conversationType\n payload[\"sender_id\"] = \"macos-app\"\n payload[\"text\"] = text\n payload[\"requires_response\"] = true\n payload[\"prepare_reply\"] = true\n return try await post(\"channels/inbound\", body: payload)\n }\n\n func voiceStatus(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n try await post(\"voice/status\", body: runtimePayload(settings: settings, secrets: secrets))\n }\n\n func speakText(_ text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"text\"] = text\n payload[\"provider\"] = settings.ttsProvider\n payload[\"voice_id\"] = settings.voiceId\n payload[\"model\"] = settings.elevenLabsModel\n payload[\"reason\"] = \"macOS app is speaking the visible assistant response.\"\n payload[\"playback\"] = true\n return try await post(\"voice/speak\", body: payload)\n }\n\n func transcribeAudio(_ audioURL: URL, settings: AppSettings, secrets: RuntimeSecrets) async throws -> String {\n let audioData = try Data(contentsOf: audioURL)\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"audio_base64\"] = audioData.base64EncodedString()\n payload[\"filename\"] = audioURL.lastPathComponent\n payload[\"provider\"] = settings.sttProvider == \"deepgram\" ? \"deepgram\" : \"local-whisper\"\n payload[\"smart_format\"] = true\n payload[\"mime_type\"] = \"audio/mp4\"\n payload[\"reason\"] = \"macOS wake listener captured the user's post-wake task.\"\n let result: JSONValue = try await post(\"voice/transcribe\", body: payload)\n let transcript = result[\"transcript\"]?.stringValue?.trimmingCharacters(in: .whitespacesAndNewlines) ?? \"\"\n guard !transcript.isEmpty else {\n let summary = result[\"summary\"]?.stringValue ?? \"No transcript was returned.\"\n let providerErrors = result[\"provider_errors\"]?.arrayValue?.compactMap { item in\n item[\"error\"]?.stringValue\n }.joined(separator: \" \")\n let detail = providerErrors?.isEmpty == false ? \"\\(summary) \\(providerErrors ?? \"\")\" : summary\n throw AgentAPIError.requestFailed(detail)\n }\n return transcript\n }\n\n func autonomousStatus() async throws -> JSONValue {\n try await get(\"autonomous/status?limit=10\")\n }\n\n func janusStatus(limit: Int = 20) async throws -> JanusStatusResponse {\n try await get(\"janus/status?limit=\\(limit)\")\n }\n\n func janusPlannerContext(request: String = \"\") async throws -> JanusPlannerContextPreview {\n let encoded = request.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? \"\"\n return try await get(\"janus/planner-context?request=\\(encoded)\")\n }\n\n func collectorStatus(limit: Int = 10) async throws -> CollectorStatusResponse {\n try await get(\"collectors/status?limit=\\(limit)\")\n }\n\n func declareJanusTaskContext(_ draft: JanusTaskContextDraft) async throws -> JSONValue {\n var payload: [String: Any] = [\n \"goal\": draft.goal,\n \"summary\": draft.summary,\n \"privacy_mode\": draft.privacyMode,\n \"allowed_help\": draft.allowedHelpList,\n \"source\": \"macos_app\"\n ]\n removeEmptyStrings(from: &payload)\n return try await post(\"janus/task-contexts\", body: payload)\n }\n\n func createJanusMutedScope(_ draft: JanusMutedScopeDraft) async throws -> JSONValue {\n var payload: [String: Any] = [\n \"mode\": draft.mode,\n \"scope_type\": draft.scopeType,\n \"entity_refs\": draft.entityRefList,\n \"collector\": draft.collector,\n \"source\": draft.source,\n \"stimulus_type\": draft.stimulusType,\n \"expires_at\": draft.expiresAt,\n \"reason\": draft.reason\n ]\n removeEmptyStrings(from: &payload)\n return try await post(\"janus/muted-scopes\", body: payload)\n }\n\n func approveJanusDeepDive(requestID: String, reason: String) async throws -> JSONValue {\n try await post(\n \"janus/deep-dives/approve\",\n body: [\n \"request_id\": requestID,\n \"reason\": reason\n ]\n )\n }\n\n func rejectJanusDeepDive(requestID: String, reason: String) async throws -> JSONValue {\n try await post(\n \"janus/deep-dives/reject\",\n body: [\n \"request_id\": requestID,\n \"reason\": reason\n ]\n )\n }\n\n func recordJanusCorrection(\n correctionType: String,\n targetType: String,\n targetID: String,\n note: String,\n taskContext: JanusTaskContextDraft? = nil,\n mutedScope: JanusMutedScopeDraft? = nil\n ) async throws -> JSONValue {\n var payload: [String: Any] = [\n \"correction_type\": correctionType,\n \"target_type\": targetType,\n \"target_id\": targetID,\n \"note\": note\n ]\n if let taskContext, taskContext.hasContext {\n payload[\"goal\"] = taskContext.goal\n payload[\"summary\"] = taskContext.summary\n payload[\"privacy_mode\"] = taskContext.privacyMode\n payload[\"allowed_help\"] = taskContext.allowedHelpList\n }\n if let mutedScope, mutedScope.hasScope {\n payload[\"mode\"] = mutedScope.mode\n payload[\"scope_type\"] = mutedScope.scopeType\n payload[\"entity_refs\"] = mutedScope.entityRefList\n payload[\"collector\"] = mutedScope.collector\n payload[\"source\"] = mutedScope.source\n payload[\"stimulus_type\"] = mutedScope.stimulusType\n payload[\"expires_at\"] = mutedScope.expiresAt\n }\n removeEmptyStrings(from: &payload)\n return try await post(\n \"janus/corrections\",\n body: payload\n )\n }\n\n func cancelJanusMutedScope(scopeID: String, reason: String) async throws -> JSONValue {\n try await post(\n \"janus/muted-scopes/cancel\",\n body: [\n \"scope_id\": scopeID,\n \"reason\": reason\n ]\n )\n }\n\n func runAutonomousCycle(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"max_cycles\"] = settings.maxCycles\n payload[\"idle_sleep_seconds\"] = 0\n payload[\"stop_after_idle_cycles\"] = 1\n payload[\"allow_initiative\"] = settings.allowInitiative\n return try await post(\"autonomous/cycles\", body: payload)\n }\n\n func sendStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> AgentRunResponse {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n let voiceFriendly = isVoiceRequest(source: source, responseMode: responseMode)\n payload[\"text\"] = formattedRequestText(text, source: source, responseMode: responseMode)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": voiceFriendly ? \"voice_friendly\" : \"detailed\",\n \"response_style\": voiceFriendly ? \"spoken_conversation\" : \"written_markdown\"\n ]\n return try await post(\"stimuli\", body: payload)\n }\n\n func streamStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) throws -> AsyncThrowingStream<AgentStreamEvent, Error> {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n let voiceFriendly = isVoiceRequest(source: source, responseMode: responseMode)\n payload[\"text\"] = formattedRequestText(text, source: source, responseMode: responseMode)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": voiceFriendly ? \"voice_friendly\" : \"detailed\",\n \"response_style\": voiceFriendly ? \"spoken_conversation\" : \"written_markdown\"\n ]\n\n var request = URLRequest(url: try routeURL(\"stimuli/stream\"))\n request.httpMethod = \"POST\"\n request.timeoutInterval = 300\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: payload, options: [])\n\n return AsyncThrowingStream { continuation in\n let streamTask = Task {\n do {\n let (bytes, response) = try await URLSession.shared.bytes(for: request)\n if let http = response as? HTTPURLResponse, !(200..<300).contains(http.statusCode) {\n throw AgentAPIError.requestFailed(\"stimuli/stream failed with HTTP \\(http.statusCode).\")\n }\n var parser = ServerSentEventParser()\n for try await line in bytes.lines {\n if Task.isCancelled { break }\n if let event = try parser.consume(line) {\n continuation.yield(event)\n }\n }\n if Task.isCancelled {\n continuation.finish(throwing: CancellationError())\n return\n }\n if let event = try parser.finish() {\n continuation.yield(event)\n }\n continuation.finish()\n } catch {\n continuation.finish(throwing: error)\n }\n }\n continuation.onTermination = { _ in\n streamTask.cancel()\n }\n }\n }\n\n func approve(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/approve\", body: [\"note\": note])\n }\n\n func reject(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/reject\", body: [\"note\": note])\n }\n\n func cancelRun(_ runID: String, reason: String) async throws -> JSONValue {\n try await post(\"runs/\\(runID)/cancel\", body: [\"reason\": reason])\n }\n\n func runTimeline(_ runID: String) async throws -> JSONValue {\n try await get(\"runs/\\(runID)/timeline?limit=100\")\n }\n\n private func get<T: Decodable>(_ route: String) async throws -> T {\n let request = URLRequest(url: try routeURL(route))\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func post<T: Decodable>(_ route: String, body: [String: Any]) async throws -> T {\n var request = URLRequest(url: try routeURL(route))\n request.httpMethod = \"POST\"\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: body, options: [])\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func routeURL(_ route: String) throws -> URL {\n guard let url = URL(string: route, relativeTo: baseURL)?.absoluteURL else {\n throw AgentAPIError.requestFailed(\"Invalid API route: \\(route)\")\n }\n return url\n }\n\n private func validate(response: URLResponse, data: Data) throws {\n guard let http = response as? HTTPURLResponse, (200..<300).contains(http.statusCode) else {\n let message = String(data: data, encoding: .utf8) ?? \"Request failed.\"\n throw AgentAPIError.requestFailed(message)\n }\n }\n\n private func formattedRequestText(_ text: String, source: String, responseMode: String) -> String {\n let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)\n guard !trimmed.isEmpty else { return text }\n let instruction = isVoiceRequest(source: source, responseMode: responseMode)\n ? voiceResponseFormattingInstruction\n : responseFormattingInstruction\n return \"\"\"\n \\(trimmed)\n\n Response formatting requirements:\n \\(instruction)\n \"\"\"\n }\n\n private func isVoiceRequest(source: String, responseMode: String) -> Bool {\n let normalizedSource = source.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()\n let normalizedMode = responseMode.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()\n return normalizedSource.contains(\"voice\") || normalizedMode.contains(\"voice\")\n }\n\n private func runtimePayload(settings: AppSettings, secrets: RuntimeSecrets) -> [String: Any] {\n var payload: [String: Any] = [\n \"planner\": settings.planner,\n \"approve_high_risk\": settings.approveHighRisk,\n \"model_provider\": apiModelProvider(settings.modelProvider),\n \"model\": settings.modelName,\n \"model_api_key_env\": modelKeyName(settings.modelProvider)\n ]\n if !settings.modelBaseURL.isEmpty {\n payload[\"model_base_url\"] = settings.modelBaseURL\n }\n let activeProvider = settings.janusModelProvider.trimmingCharacters(in: .whitespacesAndNewlines)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n payload[\"janus_model_provider\"] = apiModelProvider(activeProvider)\n payload[\"janus_model_api_key_env\"] = modelKeyName(activeProvider)\n if !settings.janusModelName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n payload[\"janus_model\"] = settings.janusModelName\n }\n if !settings.janusModelBaseURL.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n payload[\"janus_model_base_url\"] = settings.janusModelBaseURL\n }\n }\n\n var secretPayload: [String: String] = [:]\n addSecret(&secretPayload, name: modelKeyName(settings.modelProvider), value: secrets.modelAPIKey)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n addSecret(&secretPayload, name: modelKeyName(activeProvider), value: secrets.janusModelAPIKey)\n }\n addSecret(&secretPayload, name: \"DEEPGRAM_API_KEY\", value: secrets.deepgramAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_API_KEY\", value: secrets.elevenLabsAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_VOICE_ID\", value: settings.voiceId)\n addSecret(&secretPayload, name: \"ELEVENLABS_MODEL_ID\", value: settings.elevenLabsModel)\n for channel in settings.channels {\n addSecret(&secretPayload, name: channel.secretName, value: channel.secretValue)\n for item in channel.secretValues {\n addSecret(&secretPayload, name: item.key, value: item.value)\n }\n }\n if !secretPayload.isEmpty {\n payload[\"runtime_secrets\"] = secretPayload\n }\n return payload\n }\n\n private func addSecret(_ secrets: inout [String: String], name: String, value: String) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n let cleanValue = value.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, !cleanValue.isEmpty {\n secrets[cleanName] = cleanValue\n }\n }\n\n private func modelKeyName(_ provider: String) -> String {\n switch provider {\n case \"openrouter\": \"OPENROUTER_API_KEY\"\n case \"nous\": \"NOUS_API_KEY\"\n case \"novita\": \"NOVITA_API_KEY\"\n case \"lmstudio\": \"LM_API_KEY\"\n case \"anthropic\": \"ANTHROPIC_API_KEY\"\n case \"alibaba\": \"DASHSCOPE_API_KEY\"\n case \"groq\": \"GROQ_API_KEY\"\n case \"grok\", \"xai\": \"XAI_API_KEY\"\n case \"gemini\": \"GOOGLE_API_KEY\"\n case \"deepseek\": \"DEEPSEEK_API_KEY\"\n case \"mistral\": \"MISTRAL_API_KEY\"\n case \"cerebras\": \"CEREBRAS_API_KEY\"\n case \"ollama\": \"OLLAMA_API_KEY\"\n case \"ollama-cloud\": \"OLLAMA_API_KEY\"\n case \"local-openai\": \"LOCAL_LLM_API_KEY\"\n case \"vercel\": \"AI_GATEWAY_API_KEY\"\n case \"litellm\": \"LITELLM_API_KEY\"\n case \"nvidia\": \"NVIDIA_API_KEY\"\n case \"huggingface\": \"HF_TOKEN\"\n case \"zai\": \"GLM_API_KEY\"\n case \"kimi-coding\": \"KIMI_API_KEY\"\n case \"kimi-coding-cn\": \"KIMI_CN_API_KEY\"\n case \"stepfun\": \"STEPFUN_API_KEY\"\n case \"minimax\": \"MINIMAX_API_KEY\"\n case \"minimax-cn\": \"MINIMAX_CN_API_KEY\"\n case \"arcee\": \"ARCEEAI_API_KEY\"\n case \"gmi\": \"GMI_API_KEY\"\n case \"xiaomi\": \"XIAOMI_API_KEY\"\n case \"tencent-tokenhub\": \"TOKENHUB_API_KEY\"\n case \"opencode-zen\": \"OPENCODE_ZEN_API_KEY\"\n case \"opencode-go\": \"OPENCODE_GO_API_KEY\"\n case \"kilocode\": \"KILOCODE_API_KEY\"\n case \"azure-openai\": \"AZURE_OPENAI_API_KEY\"\n case \"azure-foundry\": \"AZURE_FOUNDRY_API_KEY\"\n case \"copilot\", \"copilot-acp\": \"GITHUB_TOKEN\"\n case \"bedrock\": \"AWS_ACCESS_KEY_ID\"\n case \"browser-use-cloud\": \"BROWSER_USE_API_KEY\"\n default: \"OPENAI_API_KEY\"\n }\n }\n\n private func apiModelProvider(_ provider: String) -> String {\n provider == \"openai\" ? \"openai-responses\" : provider\n }\n\n private func channelSecretRefs(_ setup: ChannelSetup) -> [String: String] {\n var refs: [String: String] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n refs[\"primary\"] = primary\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n refs[cleanName] = cleanName\n }\n }\n return refs\n }\n\n private func channelSecretConfigured(_ setup: ChannelSetup) -> [String: Bool] {\n var configured: [String: Bool] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n configured[\"primary\"] = setup.secretConfigured || !setup.secretValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n configured[cleanName] = !setup.secretValues[cleanName, default: \"\"].trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n }\n return configured\n }\n\n private func cleanList(_ values: [String]) -> [String] {\n values.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }.filter { !$0.isEmpty }\n }\n\n private func removeEmptyStrings(from payload: inout [String: Any]) {\n for (key, value) in payload {\n if let string = value as? String, string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n payload.removeValue(forKey: key)\n }\n if let strings = value as? [String], strings.isEmpty {\n payload.removeValue(forKey: key)\n }\n }\n }\n\n private static func normalizedBaseURL(_ value: String, fallback: URL = URL(string: \"http://127.0.0.1:8765/\")!) -> URL {\n let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)\n .trimmingCharacters(in: CharacterSet(charactersIn: \"/\"))\n guard !trimmed.isEmpty, let url = URL(string: \"\\(trimmed)/\") else {\n return fallback\n }\n return url\n }\n\n private static func urlEncode(_ value: String) -> String {\n value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? value\n }\n}\n\nenum AgentAPIError: LocalizedError {\n case requestFailed(String)\n\n var errorDescription: String? {\n switch self {\n case .requestFailed(let message): message\n }\n }\n}\n\nprivate struct ServerSentEventParser {\n private var eventName = \"message\"\n private var dataLines: [String] = []\n private let decoder = JSONDecoder()\n\n mutating func consume(_ line: String) throws -> AgentStreamEvent? {\n if line.hasPrefix(\"event: \") {\n eventName = String(line.dropFirst(\"event: \".count)).trimmingCharacters(in: .whitespaces)\n return nil\n }\n if line.hasPrefix(\"data: \") {\n dataLines.append(String(line.dropFirst(\"data: \".count)))\n return try finish()\n }\n if line.isEmpty {\n return try finish()\n }\n return nil\n }\n\n mutating func finish() throws -> AgentStreamEvent? {\n guard !dataLines.isEmpty else { return nil }\n let data = Data(dataLines.joined(separator: \"\\n\").utf8)\n let value = try decoder.decode(JSONValue.self, from: data)\n let event = AgentStreamEvent(event: eventName, data: value)\n eventName = \"message\"\n dataLines = []\n return event\n }\n}\n"}, "13f9ce22c0769a85c96e0473c2fc10d139b8610b": {"t": 1785756638.468247, "n": 31808, "h": "88fa482ea90c6e5b", "c": "import Foundation\n\nstruct RuntimeSecrets {\n var modelAPIKey = \"\"\n var janusModelAPIKey = \"\"\n var deepgramAPIKey = \"\"\n var elevenLabsAPIKey = \"\"\n}\n\n@MainActor\nfinal class AgentAPIClient {\n private var baseURL: URL\n private let decoder: JSONDecoder\n private let encoder = JSONEncoder()\n private let responseFormattingInstruction = \"\"\"\n Respond with a detailed final answer in GitHub-flavored Markdown. Use clear sections, bullets, and tables where they make the answer easier to scan. Include caveats, source/tool evidence, and next steps when relevant. Do not return a single unformatted paragraph unless the user explicitly asks for a very short answer.\n \"\"\"\n private let voiceResponseFormattingInstruction = \"\"\"\n The user is speaking by voice. Reply warmly and conversationally in a voice-friendly style: lead with the direct answer, keep sentences short, use short paragraphs, avoid tables unless essential, and use bullets only when they make the spoken answer clearer. Keep the final answer easy to read aloud.\n \"\"\"\n\n init(baseURL: String) {\n self.baseURL = AgentAPIClient.normalizedBaseURL(baseURL)\n decoder = JSONDecoder()\n }\n\n func setBaseURL(_ value: String) {\n baseURL = AgentAPIClient.normalizedBaseURL(value, fallback: baseURL)\n }\n\n func health() async throws -> HealthPayload {\n try await get(\"health\")\n }\n\n func systemStatus() async throws -> JSONValue {\n try await get(\"system/status\")\n }\n\n func latestUpdate() async throws -> UpdateInfo {\n try await get(\"updates/latest?platform=macos\")\n }\n\n func tools() async throws -> ToolCatalog {\n try await get(\"tools\")\n }\n\n func modelProviders() async throws -> ModelProviderCatalog {\n try await get(\"model/providers\")\n }\n\n func runs() async throws -> [RunItem] {\n try await get(\"runs?limit=30\")\n }\n\n func chatConversations() async throws -> [ChatConversation] {\n let envelope: ChatConversationEnvelope = try await get(\"chats?limit=80\")\n return envelope.conversations\n }\n\n func createChat(title: String = \"New chat\", source: String = \"macos_app\") async throws -> ChatConversation {\n let response: ChatConversationResponse = try await post(\n \"chats\",\n body: [\n \"title\": title,\n \"source\": source,\n \"metadata\": [\"client\": \"macos\"]\n ]\n )\n return response.conversation\n }\n\n func chatMessages(conversationID: String) async throws -> ChatMessagesEnvelope {\n try await get(\"chats/\\(Self.urlEncode(conversationID))/messages?limit=300\")\n }\n\n func startChatRun(conversationID: String, text: String, displayText: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> ChatRunQueuedResponse {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n let voiceFriendly = isVoiceRequest(source: source, responseMode: responseMode)\n payload[\"request\"] = formattedRequestText(text, source: source, responseMode: responseMode)\n payload[\"display_text\"] = displayText\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": voiceFriendly ? \"voice_friendly\" : \"detailed\",\n \"response_style\": voiceFriendly ? \"spoken_conversation\" : \"written_markdown\",\n \"client\": \"macos\"\n ]\n return try await post(\"chats/\\(Self.urlEncode(conversationID))/runs/async\", body: payload)\n }\n\n func approvals() async throws -> [ApprovalItem] {\n try await get(\"approvals?status=pending&limit=30\")\n }\n\n func channels() async throws -> [ChannelInfo] {\n try await get(\"channels\")\n }\n\n func connectors() async throws -> ConnectorCatalog {\n try await get(\"connectors\")\n }\n\n func configureConnector(providerID: String, credentials: [String: String], redirectURI: String) async throws -> JSONValue {\n try await post(\n \"connectors/configure\",\n body: [\n \"provider_id\": providerID,\n \"credentials\": credentials,\n \"redirect_uri\": redirectURI\n ]\n )\n }\n\n func prepareConnector(providerID: String) async throws -> ConnectorAuthorization {\n try await post(\"connectors/connect\", body: [\"provider_id\": providerID])\n }\n\n func refreshConnector(providerID: String) async throws -> JSONValue {\n try await post(\"connectors/refresh\", body: [\"provider_id\": providerID])\n }\n\n func disconnectConnector(providerID: String) async throws -> JSONValue {\n try await post(\"connectors/disconnect\", body: [\"provider_id\": providerID])\n }\n\n func outbox() async throws -> OutboxEnvelope {\n try await get(\"channels/outbox\")\n }\n\n func channelStatus(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/status\", body: payload)\n }\n\n func channelDoctor(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/doctor\", body: payload)\n }\n\n func channelRequirements(channelID: String) async throws -> JSONValue {\n try await get(\"channels/requirements?channel_id=\\(Self.urlEncode(channelID))\")\n }\n\n func runChannelSmoke(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"prepare_messages\"] = true\n payload[\"dry_run_sends\"] = true\n return try await post(\"channels/smoke\", body: payload)\n }\n\n func channelListeners(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/listeners\", body: payload)\n }\n\n func tickChannelListener(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func tickAllChannelListeners(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func saveChannelSetup(channel: ChannelInfo, setup: ChannelSetup) async throws -> JSONValue {\n let payload: [String: Any] = [\n \"channel_id\": channel.channelId,\n \"enabled\": setup.enabled,\n \"listen_enabled\": setup.listenEnabled,\n \"conversation_defaults\": [\n \"conversation_id\": setup.conversationId,\n \"conversation_type\": setup.conversationType\n ],\n \"secret_refs\": channelSecretRefs(setup),\n \"secret_configured\": channelSecretConfigured(setup),\n \"allowlist\": cleanList(setup.allowlist),\n \"group_allowlist\": cleanList(setup.groupAllowlist),\n \"notes\": setup.notes\n ]\n return try await post(\"channels/setup\", body: payload)\n }\n\n func prepareChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Prepared from the macOS app channel panel.\"\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/prepare\", body: payload)\n }\n\n func sendChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Approval-gated send from the macOS app channel panel.\"\n payload[\"approve_high_risk\"] = settings.approveHighRisk\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/send\", body: payload)\n }\n\n func sendChannelInbound(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"conversation_type\"] = setup.conversationType.isEmpty ? \"dm\" : setup.conversationType\n payload[\"sender_id\"] = \"macos-app\"\n payload[\"text\"] = text\n payload[\"requires_response\"] = true\n payload[\"prepare_reply\"] = true\n return try await post(\"channels/inbound\", body: payload)\n }\n\n func voiceStatus(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n try await post(\"voice/status\", body: runtimePayload(settings: settings, secrets: secrets))\n }\n\n func speakText(_ text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"text\"] = text\n payload[\"provider\"] = settings.ttsProvider\n payload[\"voice_id\"] = settings.voiceId\n payload[\"model\"] = settings.elevenLabsModel\n payload[\"reason\"] = \"macOS app is speaking the visible assistant response.\"\n payload[\"playback\"] = true\n return try await post(\"voice/speak\", body: payload)\n }\n\n func transcribeAudio(_ audioURL: URL, settings: AppSettings, secrets: RuntimeSecrets) async throws -> String {\n let audioData = try Data(contentsOf: audioURL)\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"audio_base64\"] = audioData.base64EncodedString()\n payload[\"filename\"] = audioURL.lastPathComponent\n payload[\"provider\"] = settings.sttProvider == \"deepgram\" ? \"deepgram\" : \"local-whisper\"\n payload[\"smart_format\"] = true\n payload[\"mime_type\"] = \"audio/mp4\"\n payload[\"reason\"] = \"macOS wake listener captured the user's post-wake task.\"\n let result: JSONValue = try await post(\"voice/transcribe\", body: payload)\n let transcript = result[\"transcript\"]?.stringValue?.trimmingCharacters(in: .whitespacesAndNewlines) ?? \"\"\n guard !transcript.isEmpty else {\n let summary = result[\"summary\"]?.stringValue ?? \"No transcript was returned.\"\n let providerErrors = result[\"provider_errors\"]?.arrayValue?.compactMap { item in\n item[\"error\"]?.stringValue\n }.joined(separator: \" \")\n let detail = providerErrors?.isEmpty == false ? \"\\(summary) \\(providerErrors ?? \"\")\" : summary\n throw AgentAPIError.requestFailed(detail)\n }\n return transcript\n }\n\n func autonomousStatus() async throws -> JSONValue {\n try await get(\"autonomous/status?limit=10\")\n }\n\n func janusStatus(limit: Int = 20) async throws -> JanusStatusResponse {\n try await get(\"janus/status?limit=\\(limit)\")\n }\n\n func janusPlannerContext(request: String = \"\") async throws -> JanusPlannerContextPreview {\n let encoded = request.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? \"\"\n return try await get(\"janus/planner-context?request=\\(encoded)\")\n }\n\n func collectorStatus(limit: Int = 10) async throws -> CollectorStatusResponse {\n try await get(\"collectors/status?limit=\\(limit)\")\n }\n\n func declareJanusTaskContext(_ draft: JanusTaskContextDraft) async throws -> JSONValue {\n var payload: [String: Any] = [\n \"goal\": draft.goal,\n \"summary\": draft.summary,\n \"privacy_mode\": draft.privacyMode,\n \"allowed_help\": draft.allowedHelpList,\n \"source\": \"macos_app\"\n ]\n removeEmptyStrings(from: &payload)\n return try await post(\"janus/task-contexts\", body: payload)\n }\n\n func createJanusMutedScope(_ draft: JanusMutedScopeDraft) async throws -> JSONValue {\n var payload: [String: Any] = [\n \"mode\": draft.mode,\n \"scope_type\": draft.scopeType,\n \"entity_refs\": draft.entityRefList,\n \"collector\": draft.collector,\n \"source\": draft.source,\n \"stimulus_type\": draft.stimulusType,\n \"expires_at\": draft.expiresAt,\n \"reason\": draft.reason\n ]\n removeEmptyStrings(from: &payload)\n return try await post(\"janus/muted-scopes\", body: payload)\n }\n\n func approveJanusDeepDive(requestID: String, reason: String) async throws -> JSONValue {\n try await post(\n \"janus/deep-dives/approve\",\n body: [\n \"request_id\": requestID,\n \"reason\": reason\n ]\n )\n }\n\n func rejectJanusDeepDive(requestID: String, reason: String) async throws -> JSONValue {\n try await post(\n \"janus/deep-dives/reject\",\n body: [\n \"request_id\": requestID,\n \"reason\": reason\n ]\n )\n }\n\n func recordJanusCorrection(\n correctionType: String,\n targetType: String,\n targetID: String,\n note: String,\n taskContext: JanusTaskContextDraft? = nil,\n mutedScope: JanusMutedScopeDraft? = nil\n ) async throws -> JSONValue {\n var payload: [String: Any] = [\n \"correction_type\": correctionType,\n \"target_type\": targetType,\n \"target_id\": targetID,\n \"note\": note\n ]\n if let taskContext, taskContext.hasContext {\n payload[\"goal\"] = taskContext.goal\n payload[\"summary\"] = taskContext.summary\n payload[\"privacy_mode\"] = taskContext.privacyMode\n payload[\"allowed_help\"] = taskContext.allowedHelpList\n }\n if let mutedScope, mutedScope.hasScope {\n payload[\"mode\"] = mutedScope.mode\n payload[\"scope_type\"] = mutedScope.scopeType\n payload[\"entity_refs\"] = mutedScope.entityRefList\n payload[\"collector\"] = mutedScope.collector\n payload[\"source\"] = mutedScope.source\n payload[\"stimulus_type\"] = mutedScope.stimulusType\n payload[\"expires_at\"] = mutedScope.expiresAt\n }\n removeEmptyStrings(from: &payload)\n return try await post(\n \"janus/corrections\",\n body: payload\n )\n }\n\n func cancelJanusMutedScope(scopeID: String, reason: String) async throws -> JSONValue {\n try await post(\n \"janus/muted-scopes/cancel\",\n body: [\n \"scope_id\": scopeID,\n \"reason\": reason\n ]\n )\n }\n\n func runAutonomousCycle(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"max_cycles\"] = settings.maxCycles\n payload[\"idle_sleep_seconds\"] = 0\n payload[\"stop_after_idle_cycles\"] = 1\n payload[\"allow_initiative\"] = settings.allowInitiative\n return try await post(\"autonomous/cycles\", body: payload)\n }\n\n func sendStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> AgentRunResponse {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n let voiceFriendly = isVoiceRequest(source: source, responseMode: responseMode)\n payload[\"text\"] = formattedRequestText(text, source: source, responseMode: responseMode)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": voiceFriendly ? \"voice_friendly\" : \"detailed\",\n \"response_style\": voiceFriendly ? \"spoken_conversation\" : \"written_markdown\"\n ]\n return try await post(\"stimuli\", body: payload)\n }\n\n func streamStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) throws -> AsyncThrowingStream<AgentStreamEvent, Error> {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n let voiceFriendly = isVoiceRequest(source: source, responseMode: responseMode)\n payload[\"text\"] = formattedRequestText(text, source: source, responseMode: responseMode)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": voiceFriendly ? \"voice_friendly\" : \"detailed\",\n \"response_style\": voiceFriendly ? \"spoken_conversation\" : \"written_markdown\"\n ]\n\n var request = URLRequest(url: try routeURL(\"stimuli/stream\"))\n request.httpMethod = \"POST\"\n request.timeoutInterval = 300\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: payload, options: [])\n\n return AsyncThrowingStream { continuation in\n let streamTask = Task {\n do {\n let (bytes, response) = try await URLSession.shared.bytes(for: request)\n if let http = response as? HTTPURLResponse, !(200..<300).contains(http.statusCode) {\n throw AgentAPIError.requestFailed(\"stimuli/stream failed with HTTP \\(http.statusCode).\")\n }\n var parser = ServerSentEventParser()\n for try await line in bytes.lines {\n if Task.isCancelled { break }\n if let event = try parser.consume(line) {\n continuation.yield(event)\n }\n }\n if Task.isCancelled {\n continuation.finish(throwing: CancellationError())\n return\n }\n if let event = try parser.finish() {\n continuation.yield(event)\n }\n continuation.finish()\n } catch {\n continuation.finish(throwing: error)\n }\n }\n continuation.onTermination = { _ in\n streamTask.cancel()\n }\n }\n }\n\n func approve(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/approve\", body: [\"note\": note])\n }\n\n func reject(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/reject\", body: [\"note\": note])\n }\n\n func cancelRun(_ runID: String, reason: String) async throws -> JSONValue {\n try await post(\"runs/\\(runID)/cancel\", body: [\"reason\": reason])\n }\n\n func runTimeline(_ runID: String) async throws -> JSONValue {\n try await get(\"runs/\\(runID)/timeline?limit=100\")\n }\n\n private func get<T: Decodable>(_ route: String) async throws -> T {\n let request = URLRequest(url: try routeURL(route))\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func post<T: Decodable>(_ route: String, body: [String: Any]) async throws -> T {\n var request = URLRequest(url: try routeURL(route))\n request.httpMethod = \"POST\"\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: body, options: [])\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func routeURL(_ route: String) throws -> URL {\n guard let url = URL(string: route, relativeTo: baseURL)?.absoluteURL else {\n throw AgentAPIError.requestFailed(\"Invalid API route: \\(route)\")\n }\n return url\n }\n\n private func validate(response: URLResponse, data: Data) throws {\n guard let http = response as? HTTPURLResponse, (200..<300).contains(http.statusCode) else {\n let message = String(data: data, encoding: .utf8) ?? \"Request failed.\"\n throw AgentAPIError.requestFailed(message)\n }\n }\n\n private func formattedRequestText(_ text: String, source: String, responseMode: String) -> String {\n let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)\n guard !trimmed.isEmpty else { return text }\n let instruction = isVoiceRequest(source: source, responseMode: responseMode)\n ? voiceResponseFormattingInstruction\n : responseFormattingInstruction\n return \"\"\"\n \\(trimmed)\n\n Response formatting requirements:\n \\(instruction)\n \"\"\"\n }\n\n private func isVoiceRequest(source: String, responseMode: String) -> Bool {\n let normalizedSource = source.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()\n let normalizedMode = responseMode.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()\n return normalizedSource.contains(\"voice\") || normalizedMode.contains(\"voice\")\n }\n\n private func runtimePayload(settings: AppSettings, secrets: RuntimeSecrets) -> [String: Any] {\n var payload: [String: Any] = [\n \"planner\": settings.planner,\n \"approve_high_risk\": settings.approveHighRisk,\n \"model_provider\": apiModelProvider(settings.modelProvider),\n \"model\": settings.modelName,\n \"model_api_key_env\": modelKeyName(settings.modelProvider)\n ]\n if !settings.modelBaseURL.isEmpty {\n payload[\"model_base_url\"] = settings.modelBaseURL\n }\n let activeProvider = settings.janusModelProvider.trimmingCharacters(in: .whitespacesAndNewlines)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n payload[\"janus_model_provider\"] = apiModelProvider(activeProvider)\n payload[\"janus_model_api_key_env\"] = modelKeyName(activeProvider)\n if !settings.janusModelName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n payload[\"janus_model\"] = settings.janusModelName\n }\n if !settings.janusModelBaseURL.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n payload[\"janus_model_base_url\"] = settings.janusModelBaseURL\n }\n }\n\n var secretPayload: [String: String] = [:]\n addSecret(&secretPayload, name: modelKeyName(settings.modelProvider), value: secrets.modelAPIKey)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n addSecret(&secretPayload, name: modelKeyName(activeProvider), value: secrets.janusModelAPIKey)\n }\n addSecret(&secretPayload, name: \"DEEPGRAM_API_KEY\", value: secrets.deepgramAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_API_KEY\", value: secrets.elevenLabsAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_VOICE_ID\", value: settings.voiceId)\n addSecret(&secretPayload, name: \"ELEVENLABS_MODEL_ID\", value: settings.elevenLabsModel)\n for channel in settings.channels {\n addSecret(&secretPayload, name: channel.secretName, value: channel.secretValue)\n for item in channel.secretValues {\n addSecret(&secretPayload, name: item.key, value: item.value)\n }\n }\n if !secretPayload.isEmpty {\n payload[\"runtime_secrets\"] = secretPayload\n }\n return payload\n }\n\n private func addSecret(_ secrets: inout [String: String], name: String, value: String) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n let cleanValue = value.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, !cleanValue.isEmpty {\n secrets[cleanName] = cleanValue\n }\n }\n\n private func modelKeyName(_ provider: String) -> String {\n switch provider {\n case \"openrouter\": \"OPENROUTER_API_KEY\"\n case \"nous\": \"NOUS_API_KEY\"\n case \"novita\": \"NOVITA_API_KEY\"\n case \"lmstudio\": \"LM_API_KEY\"\n case \"anthropic\": \"ANTHROPIC_API_KEY\"\n case \"alibaba\": \"DASHSCOPE_API_KEY\"\n case \"groq\": \"GROQ_API_KEY\"\n case \"grok\", \"xai\": \"XAI_API_KEY\"\n case \"gemini\": \"GOOGLE_API_KEY\"\n case \"deepseek\": \"DEEPSEEK_API_KEY\"\n case \"mistral\": \"MISTRAL_API_KEY\"\n case \"cerebras\": \"CEREBRAS_API_KEY\"\n case \"ollama\": \"OLLAMA_API_KEY\"\n case \"ollama-cloud\": \"OLLAMA_API_KEY\"\n case \"local-openai\": \"LOCAL_LLM_API_KEY\"\n case \"vercel\": \"AI_GATEWAY_API_KEY\"\n case \"litellm\": \"LITELLM_API_KEY\"\n case \"nvidia\": \"NVIDIA_API_KEY\"\n case \"huggingface\": \"HF_TOKEN\"\n case \"zai\": \"GLM_API_KEY\"\n case \"kimi-coding\": \"KIMI_API_KEY\"\n case \"kimi-coding-cn\": \"KIMI_CN_API_KEY\"\n case \"stepfun\": \"STEPFUN_API_KEY\"\n case \"minimax\": \"MINIMAX_API_KEY\"\n case \"minimax-cn\": \"MINIMAX_CN_API_KEY\"\n case \"arcee\": \"ARCEEAI_API_KEY\"\n case \"gmi\": \"GMI_API_KEY\"\n case \"xiaomi\": \"XIAOMI_API_KEY\"\n case \"tencent-tokenhub\": \"TOKENHUB_API_KEY\"\n case \"opencode-zen\": \"OPENCODE_ZEN_API_KEY\"\n case \"opencode-go\": \"OPENCODE_GO_API_KEY\"\n case \"kilocode\": \"KILOCODE_API_KEY\"\n case \"azure-openai\": \"AZURE_OPENAI_API_KEY\"\n case \"azure-foundry\": \"AZURE_FOUNDRY_API_KEY\"\n case \"copilot\", \"copilot-acp\": \"GITHUB_TOKEN\"\n case \"bedrock\": \"AWS_ACCESS_KEY_ID\"\n case \"browser-use-cloud\": \"BROWSER_USE_API_KEY\"\n default: \"OPENAI_API_KEY\"\n }\n }\n\n private func apiModelProvider(_ provider: String) -> String {\n provider == \"openai\" ? \"openai-responses\" : provider\n }\n\n private func channelSecretRefs(_ setup: ChannelSetup) -> [String: String] {\n var refs: [String: String] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n refs[\"primary\"] = primary\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n refs[cleanName] = cleanName\n }\n }\n return refs\n }\n\n private func channelSecretConfigured(_ setup: ChannelSetup) -> [String: Bool] {\n var configured: [String: Bool] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n configured[\"primary\"] = setup.secretConfigured || !setup.secretValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n configured[cleanName] = !setup.secretValues[cleanName, default: \"\"].trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n }\n return configured\n }\n\n private func cleanList(_ values: [String]) -> [String] {\n values.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }.filter { !$0.isEmpty }\n }\n\n private func removeEmptyStrings(from payload: inout [String: Any]) {\n for (key, value) in payload {\n if let string = value as? String, string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n payload.removeValue(forKey: key)\n }\n if let strings = value as? [String], strings.isEmpty {\n payload.removeValue(forKey: key)\n }\n }\n }\n\n private static func normalizedBaseURL(_ value: String, fallback: URL = URL(string: \"http://127.0.0.1:8765/\")!) -> URL {\n let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)\n .trimmingCharacters(in: CharacterSet(charactersIn: \"/\"))\n guard !trimmed.isEmpty, let url = URL(string: \"\\(trimmed)/\") else {\n return fallback\n }\n return url\n }\n\n private static func urlEncode(_ value: String) -> String {\n value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? value\n }\n}\n\nenum AgentAPIError: LocalizedError {\n case requestFailed(String)\n\n var errorDescription: String? {\n switch self {\n case .requestFailed(let message): message\n }\n }\n}\n\nprivate struct ServerSentEventParser {\n private var eventName = \"message\"\n private var dataLines: [String] = []\n private let decoder = JSONDecoder()\n\n mutating func consume(_ line: String) throws -> AgentStreamEvent? {\n if line.hasPrefix(\"event: \") {\n eventName = String(line.dropFirst(\"event: \".count)).trimmingCharacters(in: .whitespaces)\n return nil\n }\n if line.hasPrefix(\"data: \") {\n dataLines.append(String(line.dropFirst(\"data: \".count)))\n return try finish()\n }\n if line.isEmpty {\n return try finish()\n }\n return nil\n }\n\n mutating func finish() throws -> AgentStreamEvent? {\n guard !dataLines.isEmpty else { return nil }\n let data = Data(dataLines.joined(separator: \"\\n\").utf8)\n let value = try decoder.decode(JSONValue.self, from: data)\n let event = AgentStreamEvent(event: eventName, data: value)\n eventName = \"message\"\n dataLines = []\n return event\n }\n}\n"}, "660354c4ad316d4b976c074b2f3c114f2e11d8b0": {"t": 1785756640.005713, "n": 29968, "h": "90930f80eaf9c70f", "c": "import Foundation\n\nstruct RuntimeSecrets {\n var modelAPIKey = \"\"\n var janusModelAPIKey = \"\"\n var deepgramAPIKey = \"\"\n var elevenLabsAPIKey = \"\"\n}\n\n@MainActor\nfinal class AgentAPIClient {\n private var baseURL: URL\n private let decoder: JSONDecoder\n private let encoder = JSONEncoder()\n private let responseFormattingInstruction = \"\"\"\n Respond with a detailed final answer in GitHub-flavored Markdown. Use clear sections, bullets, and tables where they make the answer easier to scan. Include caveats, source/tool evidence, and next steps when relevant. Do not return a single unformatted paragraph unless the user explicitly asks for a very short answer.\n \"\"\"\n private let voiceResponseFormattingInstruction = \"\"\"\n The user is speaking by voice. Reply warmly and conversationally in a voice-friendly style: lead with the direct answer, keep sentences short, use short paragraphs, avoid tables unless essential, and use bullets only when they make the spoken answer clearer. Keep the final answer easy to read aloud.\n \"\"\"\n\n init(baseURL: String) {\n self.baseURL = AgentAPIClient.normalizedBaseURL(baseURL)\n decoder = JSONDecoder()\n }\n\n func setBaseURL(_ value: String) {\n baseURL = AgentAPIClient.normalizedBaseURL(value, fallback: baseURL)\n }\n\n func health() async throws -> HealthPayload {\n try await get(\"health\")\n }\n\n func systemStatus() async throws -> JSONValue {\n try await get(\"system/status\")\n }\n\n func latestUpdate() async throws -> UpdateInfo {\n try await get(\"updates/latest?platform=macos\")\n }\n\n func tools() async throws -> ToolCatalog {\n try await get(\"tools\")\n }\n\n func modelProviders() async throws -> ModelProviderCatalog {\n try await get(\"model/providers\")\n }\n\n func runs() async throws -> [RunItem] {\n try await get(\"runs?limit=30\")\n }\n\n func approvals() async throws -> [ApprovalItem] {\n try await get(\"approvals?status=pending&limit=30\")\n }\n\n func channels() async throws -> [ChannelInfo] {\n try await get(\"channels\")\n }\n\n func connectors() async throws -> ConnectorCatalog {\n try await get(\"connectors\")\n }\n\n func configureConnector(providerID: String, clientID: String, clientSecret: String, redirectURI: String) async throws -> JSONValue {\n try await post(\n \"connectors/configure\",\n body: [\n \"provider_id\": providerID,\n \"client_id\": clientID,\n \"client_secret\": clientSecret,\n \"redirect_uri\": redirectURI\n ]\n )\n }\n\n func prepareConnector(providerID: String) async throws -> ConnectorAuthorization {\n try await post(\"connectors/connect\", body: [\"provider_id\": providerID])\n }\n\n func refreshConnector(providerID: String) async throws -> JSONValue {\n try await post(\"connectors/refresh\", body: [\"provider_id\": providerID])\n }\n\n func disconnectConnector(providerID: String) async throws -> JSONValue {\n try await post(\"connectors/disconnect\", body: [\"provider_id\": providerID])\n }\n\n func outbox() async throws -> OutboxEnvelope {\n try await get(\"channels/outbox\")\n }\n\n func channelStatus(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/status\", body: payload)\n }\n\n func channelDoctor(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/doctor\", body: payload)\n }\n\n func channelRequirements(channelID: String) async throws -> JSONValue {\n try await get(\"channels/requirements?channel_id=\\(Self.urlEncode(channelID))\")\n }\n\n func runChannelSmoke(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"prepare_messages\"] = true\n payload[\"dry_run_sends\"] = true\n return try await post(\"channels/smoke\", body: payload)\n }\n\n func channelListeners(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/listeners\", body: payload)\n }\n\n func tickChannelListener(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func tickAllChannelListeners(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func saveChannelSetup(channel: ChannelInfo, setup: ChannelSetup) async throws -> JSONValue {\n let payload: [String: Any] = [\n \"channel_id\": channel.channelId,\n \"enabled\": setup.enabled,\n \"listen_enabled\": setup.listenEnabled,\n \"conversation_defaults\": [\n \"conversation_id\": setup.conversationId,\n \"conversation_type\": setup.conversationType\n ],\n \"secret_refs\": channelSecretRefs(setup),\n \"secret_configured\": channelSecretConfigured(setup),\n \"allowlist\": cleanList(setup.allowlist),\n \"group_allowlist\": cleanList(setup.groupAllowlist),\n \"notes\": setup.notes\n ]\n return try await post(\"channels/setup\", body: payload)\n }\n\n func prepareChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Prepared from the macOS app channel panel.\"\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/prepare\", body: payload)\n }\n\n func sendChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Approval-gated send from the macOS app channel panel.\"\n payload[\"approve_high_risk\"] = settings.approveHighRisk\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/send\", body: payload)\n }\n\n func sendChannelInbound(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"conversation_type\"] = setup.conversationType.isEmpty ? \"dm\" : setup.conversationType\n payload[\"sender_id\"] = \"macos-app\"\n payload[\"text\"] = text\n payload[\"requires_response\"] = true\n payload[\"prepare_reply\"] = true\n return try await post(\"channels/inbound\", body: payload)\n }\n\n func voiceStatus(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n try await post(\"voice/status\", body: runtimePayload(settings: settings, secrets: secrets))\n }\n\n func speakText(_ text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"text\"] = text\n payload[\"provider\"] = settings.ttsProvider\n payload[\"voice_id\"] = settings.voiceId\n payload[\"model\"] = settings.elevenLabsModel\n payload[\"reason\"] = \"macOS app is speaking the visible assistant response.\"\n payload[\"playback\"] = true\n return try await post(\"voice/speak\", body: payload)\n }\n\n func transcribeAudio(_ audioURL: URL, settings: AppSettings, secrets: RuntimeSecrets) async throws -> String {\n let audioData = try Data(contentsOf: audioURL)\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"audio_base64\"] = audioData.base64EncodedString()\n payload[\"filename\"] = audioURL.lastPathComponent\n payload[\"provider\"] = settings.sttProvider == \"deepgram\" ? \"deepgram\" : \"local-whisper\"\n payload[\"smart_format\"] = true\n payload[\"mime_type\"] = \"audio/mp4\"\n payload[\"reason\"] = \"macOS wake listener captured the user's post-wake task.\"\n let result: JSONValue = try await post(\"voice/transcribe\", body: payload)\n let transcript = result[\"transcript\"]?.stringValue?.trimmingCharacters(in: .whitespacesAndNewlines) ?? \"\"\n guard !transcript.isEmpty else {\n let summary = result[\"summary\"]?.stringValue ?? \"No transcript was returned.\"\n let providerErrors = result[\"provider_errors\"]?.arrayValue?.compactMap { item in\n item[\"error\"]?.stringValue\n }.joined(separator: \" \")\n let detail = providerErrors?.isEmpty == false ? \"\\(summary) \\(providerErrors ?? \"\")\" : summary\n throw AgentAPIError.requestFailed(detail)\n }\n return transcript\n }\n\n func autonomousStatus() async throws -> JSONValue {\n try await get(\"autonomous/status?limit=10\")\n }\n\n func janusStatus(limit: Int = 20) async throws -> JanusStatusResponse {\n try await get(\"janus/status?limit=\\(limit)\")\n }\n\n func janusPlannerContext(request: String = \"\") async throws -> JanusPlannerContextPreview {\n let encoded = request.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? \"\"\n return try await get(\"janus/planner-context?request=\\(encoded)\")\n }\n\n func collectorStatus(limit: Int = 10) async throws -> CollectorStatusResponse {\n try await get(\"collectors/status?limit=\\(limit)\")\n }\n\n func declareJanusTaskContext(_ draft: JanusTaskContextDraft) async throws -> JSONValue {\n var payload: [String: Any] = [\n \"goal\": draft.goal,\n \"summary\": draft.summary,\n \"privacy_mode\": draft.privacyMode,\n \"allowed_help\": draft.allowedHelpList,\n \"source\": \"macos_app\"\n ]\n removeEmptyStrings(from: &payload)\n return try await post(\"janus/task-contexts\", body: payload)\n }\n\n func createJanusMutedScope(_ draft: JanusMutedScopeDraft) async throws -> JSONValue {\n var payload: [String: Any] = [\n \"mode\": draft.mode,\n \"scope_type\": draft.scopeType,\n \"entity_refs\": draft.entityRefList,\n \"collector\": draft.collector,\n \"source\": draft.source,\n \"stimulus_type\": draft.stimulusType,\n \"expires_at\": draft.expiresAt,\n \"reason\": draft.reason\n ]\n removeEmptyStrings(from: &payload)\n return try await post(\"janus/muted-scopes\", body: payload)\n }\n\n func approveJanusDeepDive(requestID: String, reason: String) async throws -> JSONValue {\n try await post(\n \"janus/deep-dives/approve\",\n body: [\n \"request_id\": requestID,\n \"reason\": reason\n ]\n )\n }\n\n func rejectJanusDeepDive(requestID: String, reason: String) async throws -> JSONValue {\n try await post(\n \"janus/deep-dives/reject\",\n body: [\n \"request_id\": requestID,\n \"reason\": reason\n ]\n )\n }\n\n func recordJanusCorrection(\n correctionType: String,\n targetType: String,\n targetID: String,\n note: String,\n taskContext: JanusTaskContextDraft? = nil,\n mutedScope: JanusMutedScopeDraft? = nil\n ) async throws -> JSONValue {\n var payload: [String: Any] = [\n \"correction_type\": correctionType,\n \"target_type\": targetType,\n \"target_id\": targetID,\n \"note\": note\n ]\n if let taskContext, taskContext.hasContext {\n payload[\"goal\"] = taskContext.goal\n payload[\"summary\"] = taskContext.summary\n payload[\"privacy_mode\"] = taskContext.privacyMode\n payload[\"allowed_help\"] = taskContext.allowedHelpList\n }\n if let mutedScope, mutedScope.hasScope {\n payload[\"mode\"] = mutedScope.mode\n payload[\"scope_type\"] = mutedScope.scopeType\n payload[\"entity_refs\"] = mutedScope.entityRefList\n payload[\"collector\"] = mutedScope.collector\n payload[\"source\"] = mutedScope.source\n payload[\"stimulus_type\"] = mutedScope.stimulusType\n payload[\"expires_at\"] = mutedScope.expiresAt\n }\n removeEmptyStrings(from: &payload)\n return try await post(\n \"janus/corrections\",\n body: payload\n )\n }\n\n func cancelJanusMutedScope(scopeID: String, reason: String) async throws -> JSONValue {\n try await post(\n \"janus/muted-scopes/cancel\",\n body: [\n \"scope_id\": scopeID,\n \"reason\": reason\n ]\n )\n }\n\n func runAutonomousCycle(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"max_cycles\"] = settings.maxCycles\n payload[\"idle_sleep_seconds\"] = 0\n payload[\"stop_after_idle_cycles\"] = 1\n payload[\"allow_initiative\"] = settings.allowInitiative\n return try await post(\"autonomous/cycles\", body: payload)\n }\n\n func sendStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> AgentRunResponse {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n let voiceFriendly = isVoiceRequest(source: source, responseMode: responseMode)\n payload[\"text\"] = formattedRequestText(text, source: source, responseMode: responseMode)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": voiceFriendly ? \"voice_friendly\" : \"detailed\",\n \"response_style\": voiceFriendly ? \"spoken_conversation\" : \"written_markdown\"\n ]\n return try await post(\"stimuli\", body: payload)\n }\n\n func streamStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) throws -> AsyncThrowingStream<AgentStreamEvent, Error> {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n let voiceFriendly = isVoiceRequest(source: source, responseMode: responseMode)\n payload[\"text\"] = formattedRequestText(text, source: source, responseMode: responseMode)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": voiceFriendly ? \"voice_friendly\" : \"detailed\",\n \"response_style\": voiceFriendly ? \"spoken_conversation\" : \"written_markdown\"\n ]\n\n var request = URLRequest(url: try routeURL(\"stimuli/stream\"))\n request.httpMethod = \"POST\"\n request.timeoutInterval = 300\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: payload, options: [])\n\n return AsyncThrowingStream { continuation in\n let streamTask = Task {\n do {\n let (bytes, response) = try await URLSession.shared.bytes(for: request)\n if let http = response as? HTTPURLResponse, !(200..<300).contains(http.statusCode) {\n throw AgentAPIError.requestFailed(\"stimuli/stream failed with HTTP \\(http.statusCode).\")\n }\n var parser = ServerSentEventParser()\n for try await line in bytes.lines {\n if Task.isCancelled { break }\n if let event = try parser.consume(line) {\n continuation.yield(event)\n }\n }\n if Task.isCancelled {\n continuation.finish(throwing: CancellationError())\n return\n }\n if let event = try parser.finish() {\n continuation.yield(event)\n }\n continuation.finish()\n } catch {\n continuation.finish(throwing: error)\n }\n }\n continuation.onTermination = { _ in\n streamTask.cancel()\n }\n }\n }\n\n func approve(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/approve\", body: [\"note\": note])\n }\n\n func reject(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/reject\", body: [\"note\": note])\n }\n\n func cancelRun(_ runID: String, reason: String) async throws -> JSONValue {\n try await post(\"runs/\\(runID)/cancel\", body: [\"reason\": reason])\n }\n\n func runTimeline(_ runID: String) async throws -> JSONValue {\n try await get(\"runs/\\(runID)/timeline?limit=100\")\n }\n\n private func get<T: Decodable>(_ route: String) async throws -> T {\n let request = URLRequest(url: try routeURL(route))\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func post<T: Decodable>(_ route: String, body: [String: Any]) async throws -> T {\n var request = URLRequest(url: try routeURL(route))\n request.httpMethod = \"POST\"\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: body, options: [])\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func routeURL(_ route: String) throws -> URL {\n guard let url = URL(string: route, relativeTo: baseURL)?.absoluteURL else {\n throw AgentAPIError.requestFailed(\"Invalid API route: \\(route)\")\n }\n return url\n }\n\n private func validate(response: URLResponse, data: Data) throws {\n guard let http = response as? HTTPURLResponse, (200..<300).contains(http.statusCode) else {\n let message = String(data: data, encoding: .utf8) ?? \"Request failed.\"\n throw AgentAPIError.requestFailed(message)\n }\n }\n\n private func formattedRequestText(_ text: String, source: String, responseMode: String) -> String {\n let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)\n guard !trimmed.isEmpty else { return text }\n let instruction = isVoiceRequest(source: source, responseMode: responseMode)\n ? voiceResponseFormattingInstruction\n : responseFormattingInstruction\n return \"\"\"\n \\(trimmed)\n\n Response formatting requirements:\n \\(instruction)\n \"\"\"\n }\n\n private func isVoiceRequest(source: String, responseMode: String) -> Bool {\n let normalizedSource = source.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()\n let normalizedMode = responseMode.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()\n return normalizedSource.contains(\"voice\") || normalizedMode.contains(\"voice\")\n }\n\n private func runtimePayload(settings: AppSettings, secrets: RuntimeSecrets) -> [String: Any] {\n var payload: [String: Any] = [\n \"planner\": settings.planner,\n \"approve_high_risk\": settings.approveHighRisk,\n \"model_provider\": apiModelProvider(settings.modelProvider),\n \"model\": settings.modelName,\n \"model_api_key_env\": modelKeyName(settings.modelProvider)\n ]\n if !settings.modelBaseURL.isEmpty {\n payload[\"model_base_url\"] = settings.modelBaseURL\n }\n let activeProvider = settings.janusModelProvider.trimmingCharacters(in: .whitespacesAndNewlines)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n payload[\"janus_model_provider\"] = apiModelProvider(activeProvider)\n payload[\"janus_model_api_key_env\"] = modelKeyName(activeProvider)\n if !settings.janusModelName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n payload[\"janus_model\"] = settings.janusModelName\n }\n if !settings.janusModelBaseURL.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n payload[\"janus_model_base_url\"] = settings.janusModelBaseURL\n }\n }\n\n var secretPayload: [String: String] = [:]\n addSecret(&secretPayload, name: modelKeyName(settings.modelProvider), value: secrets.modelAPIKey)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n addSecret(&secretPayload, name: modelKeyName(activeProvider), value: secrets.janusModelAPIKey)\n }\n addSecret(&secretPayload, name: \"DEEPGRAM_API_KEY\", value: secrets.deepgramAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_API_KEY\", value: secrets.elevenLabsAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_VOICE_ID\", value: settings.voiceId)\n addSecret(&secretPayload, name: \"ELEVENLABS_MODEL_ID\", value: settings.elevenLabsModel)\n for channel in settings.channels {\n addSecret(&secretPayload, name: channel.secretName, value: channel.secretValue)\n for item in channel.secretValues {\n addSecret(&secretPayload, name: item.key, value: item.value)\n }\n }\n if !secretPayload.isEmpty {\n payload[\"runtime_secrets\"] = secretPayload\n }\n return payload\n }\n\n private func addSecret(_ secrets: inout [String: String], name: String, value: String) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n let cleanValue = value.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, !cleanValue.isEmpty {\n secrets[cleanName] = cleanValue\n }\n }\n\n private func modelKeyName(_ provider: String) -> String {\n switch provider {\n case \"openrouter\": \"OPENROUTER_API_KEY\"\n case \"nous\": \"NOUS_API_KEY\"\n case \"novita\": \"NOVITA_API_KEY\"\n case \"lmstudio\": \"LM_API_KEY\"\n case \"anthropic\": \"ANTHROPIC_API_KEY\"\n case \"alibaba\": \"DASHSCOPE_API_KEY\"\n case \"groq\": \"GROQ_API_KEY\"\n case \"grok\", \"xai\": \"XAI_API_KEY\"\n case \"gemini\": \"GOOGLE_API_KEY\"\n case \"deepseek\": \"DEEPSEEK_API_KEY\"\n case \"mistral\": \"MISTRAL_API_KEY\"\n case \"cerebras\": \"CEREBRAS_API_KEY\"\n case \"ollama\": \"OLLAMA_API_KEY\"\n case \"ollama-cloud\": \"OLLAMA_API_KEY\"\n case \"local-openai\": \"LOCAL_LLM_API_KEY\"\n case \"vercel\": \"AI_GATEWAY_API_KEY\"\n case \"litellm\": \"LITELLM_API_KEY\"\n case \"nvidia\": \"NVIDIA_API_KEY\"\n case \"huggingface\": \"HF_TOKEN\"\n case \"zai\": \"GLM_API_KEY\"\n case \"kimi-coding\": \"KIMI_API_KEY\"\n case \"kimi-coding-cn\": \"KIMI_CN_API_KEY\"\n case \"stepfun\": \"STEPFUN_API_KEY\"\n case \"minimax\": \"MINIMAX_API_KEY\"\n case \"minimax-cn\": \"MINIMAX_CN_API_KEY\"\n case \"arcee\": \"ARCEEAI_API_KEY\"\n case \"gmi\": \"GMI_API_KEY\"\n case \"xiaomi\": \"XIAOMI_API_KEY\"\n case \"tencent-tokenhub\": \"TOKENHUB_API_KEY\"\n case \"opencode-zen\": \"OPENCODE_ZEN_API_KEY\"\n case \"opencode-go\": \"OPENCODE_GO_API_KEY\"\n case \"kilocode\": \"KILOCODE_API_KEY\"\n case \"azure-openai\": \"AZURE_OPENAI_API_KEY\"\n case \"azure-foundry\": \"AZURE_FOUNDRY_API_KEY\"\n case \"copilot\", \"copilot-acp\": \"GITHUB_TOKEN\"\n case \"bedrock\": \"AWS_ACCESS_KEY_ID\"\n case \"browser-use-cloud\": \"BROWSER_USE_API_KEY\"\n default: \"OPENAI_API_KEY\"\n }\n }\n\n private func apiModelProvider(_ provider: String) -> String {\n provider == \"openai\" ? \"openai-responses\" : provider\n }\n\n private func channelSecretRefs(_ setup: ChannelSetup) -> [String: String] {\n var refs: [String: String] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n refs[\"primary\"] = primary\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n refs[cleanName] = cleanName\n }\n }\n return refs\n }\n\n private func channelSecretConfigured(_ setup: ChannelSetup) -> [String: Bool] {\n var configured: [String: Bool] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n configured[\"primary\"] = setup.secretConfigured || !setup.secretValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n configured[cleanName] = !setup.secretValues[cleanName, default: \"\"].trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n }\n return configured\n }\n\n private func cleanList(_ values: [String]) -> [String] {\n values.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }.filter { !$0.isEmpty }\n }\n\n private func removeEmptyStrings(from payload: inout [String: Any]) {\n for (key, value) in payload {\n if let string = value as? String, string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n payload.removeValue(forKey: key)\n }\n if let strings = value as? [String], strings.isEmpty {\n payload.removeValue(forKey: key)\n }\n }\n }\n\n private static func normalizedBaseURL(_ value: String, fallback: URL = URL(string: \"http://127.0.0.1:8765/\")!) -> URL {\n let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)\n .trimmingCharacters(in: CharacterSet(charactersIn: \"/\"))\n guard !trimmed.isEmpty, let url = URL(string: \"\\(trimmed)/\") else {\n return fallback\n }\n return url\n }\n\n private static func urlEncode(_ value: String) -> String {\n value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? value\n }\n}\n\nenum AgentAPIError: LocalizedError {\n case requestFailed(String)\n\n var errorDescription: String? {\n switch self {\n case .requestFailed(let message): message\n }\n }\n}\n\nprivate struct ServerSentEventParser {\n private var eventName = \"message\"\n private var dataLines: [String] = []\n private let decoder = JSONDecoder()\n\n mutating func consume(_ line: String) throws -> AgentStreamEvent? {\n if line.hasPrefix(\"event: \") {\n eventName = String(line.dropFirst(\"event: \".count)).trimmingCharacters(in: .whitespaces)\n return nil\n }\n if line.hasPrefix(\"data: \") {\n dataLines.append(String(line.dropFirst(\"data: \".count)))\n return try finish()\n }\n if line.isEmpty {\n return try finish()\n }\n return nil\n }\n\n mutating func finish() throws -> AgentStreamEvent? {\n guard !dataLines.isEmpty else { return nil }\n let data = Data(dataLines.joined(separator: \"\\n\").utf8)\n let value = try decoder.decode(JSONValue.self, from: data)\n let event = AgentStreamEvent(event: eventName, data: value)\n eventName = \"message\"\n dataLines = []\n return event\n }\n}\n"}, "44d724514bf0f3210ed5d0a72f73b37b69ca82f4": {"t": 1785756642.9379125, "n": 28513, "h": "4bf2135acd7c5368", "c": "import Foundation\n\nstruct RuntimeSecrets {\n var modelAPIKey = \"\"\n var janusModelAPIKey = \"\"\n var deepgramAPIKey = \"\"\n var elevenLabsAPIKey = \"\"\n}\n\n@MainActor\nfinal class AgentAPIClient {\n private var baseURL: URL\n private let decoder: JSONDecoder\n private let encoder = JSONEncoder()\n private let responseFormattingInstruction = \"\"\"\n Respond with a detailed final answer in GitHub-flavored Markdown. Use clear sections, bullets, and tables where they make the answer easier to scan. Include caveats, source/tool evidence, and next steps when relevant. Do not return a single unformatted paragraph unless the user explicitly asks for a very short answer.\n \"\"\"\n\n init(baseURL: String) {\n self.baseURL = AgentAPIClient.normalizedBaseURL(baseURL)\n decoder = JSONDecoder()\n }\n\n func setBaseURL(_ value: String) {\n baseURL = AgentAPIClient.normalizedBaseURL(value, fallback: baseURL)\n }\n\n func health() async throws -> HealthPayload {\n try await get(\"health\")\n }\n\n func systemStatus() async throws -> JSONValue {\n try await get(\"system/status\")\n }\n\n func latestUpdate() async throws -> UpdateInfo {\n try await get(\"updates/latest?platform=macos\")\n }\n\n func tools() async throws -> ToolCatalog {\n try await get(\"tools\")\n }\n\n func modelProviders() async throws -> ModelProviderCatalog {\n try await get(\"model/providers\")\n }\n\n func runs() async throws -> [RunItem] {\n try await get(\"runs?limit=30\")\n }\n\n func approvals() async throws -> [ApprovalItem] {\n try await get(\"approvals?status=pending&limit=30\")\n }\n\n func channels() async throws -> [ChannelInfo] {\n try await get(\"channels\")\n }\n\n func connectors() async throws -> ConnectorCatalog {\n try await get(\"connectors\")\n }\n\n func configureConnector(providerID: String, clientID: String, clientSecret: String, redirectURI: String) async throws -> JSONValue {\n try await post(\n \"connectors/configure\",\n body: [\n \"provider_id\": providerID,\n \"client_id\": clientID,\n \"client_secret\": clientSecret,\n \"redirect_uri\": redirectURI\n ]\n )\n }\n\n func prepareConnector(providerID: String) async throws -> ConnectorAuthorization {\n try await post(\"connectors/connect\", body: [\"provider_id\": providerID])\n }\n\n func refreshConnector(providerID: String) async throws -> JSONValue {\n try await post(\"connectors/refresh\", body: [\"provider_id\": providerID])\n }\n\n func disconnectConnector(providerID: String) async throws -> JSONValue {\n try await post(\"connectors/disconnect\", body: [\"provider_id\": providerID])\n }\n\n func outbox() async throws -> OutboxEnvelope {\n try await get(\"channels/outbox\")\n }\n\n func channelStatus(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/status\", body: payload)\n }\n\n func channelDoctor(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/doctor\", body: payload)\n }\n\n func channelRequirements(channelID: String) async throws -> JSONValue {\n try await get(\"channels/requirements?channel_id=\\(Self.urlEncode(channelID))\")\n }\n\n func runChannelSmoke(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"prepare_messages\"] = true\n payload[\"dry_run_sends\"] = true\n return try await post(\"channels/smoke\", body: payload)\n }\n\n func channelListeners(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/listeners\", body: payload)\n }\n\n func tickChannelListener(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func tickAllChannelListeners(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func saveChannelSetup(channel: ChannelInfo, setup: ChannelSetup) async throws -> JSONValue {\n let payload: [String: Any] = [\n \"channel_id\": channel.channelId,\n \"enabled\": setup.enabled,\n \"listen_enabled\": setup.listenEnabled,\n \"conversation_defaults\": [\n \"conversation_id\": setup.conversationId,\n \"conversation_type\": setup.conversationType\n ],\n \"secret_refs\": channelSecretRefs(setup),\n \"secret_configured\": channelSecretConfigured(setup),\n \"allowlist\": cleanList(setup.allowlist),\n \"group_allowlist\": cleanList(setup.groupAllowlist),\n \"notes\": setup.notes\n ]\n return try await post(\"channels/setup\", body: payload)\n }\n\n func prepareChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Prepared from the macOS app channel panel.\"\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/prepare\", body: payload)\n }\n\n func sendChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Approval-gated send from the macOS app channel panel.\"\n payload[\"approve_high_risk\"] = settings.approveHighRisk\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/send\", body: payload)\n }\n\n func sendChannelInbound(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"conversation_type\"] = setup.conversationType.isEmpty ? \"dm\" : setup.conversationType\n payload[\"sender_id\"] = \"macos-app\"\n payload[\"text\"] = text\n payload[\"requires_response\"] = true\n payload[\"prepare_reply\"] = true\n return try await post(\"channels/inbound\", body: payload)\n }\n\n func voiceStatus(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n try await post(\"voice/status\", body: runtimePayload(settings: settings, secrets: secrets))\n }\n\n func speakText(_ text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"text\"] = text\n payload[\"provider\"] = settings.ttsProvider\n payload[\"voice_id\"] = settings.voiceId\n payload[\"model\"] = settings.elevenLabsModel\n payload[\"reason\"] = \"macOS app is speaking the visible assistant response.\"\n payload[\"playback\"] = true\n return try await post(\"voice/speak\", body: payload)\n }\n\n func transcribeAudio(_ audioURL: URL, settings: AppSettings, secrets: RuntimeSecrets) async throws -> String {\n let audioData = try Data(contentsOf: audioURL)\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"audio_base64\"] = audioData.base64EncodedString()\n payload[\"filename\"] = audioURL.lastPathComponent\n payload[\"provider\"] = settings.sttProvider == \"deepgram\" ? \"deepgram\" : \"local-whisper\"\n payload[\"smart_format\"] = true\n payload[\"mime_type\"] = \"audio/mp4\"\n payload[\"reason\"] = \"macOS wake listener captured the user's post-wake task.\"\n let result: JSONValue = try await post(\"voice/transcribe\", body: payload)\n let transcript = result[\"transcript\"]?.stringValue?.trimmingCharacters(in: .whitespacesAndNewlines) ?? \"\"\n guard !transcript.isEmpty else {\n let summary = result[\"summary\"]?.stringValue ?? \"No transcript was returned.\"\n let providerErrors = result[\"provider_errors\"]?.arrayValue?.compactMap { item in\n item[\"error\"]?.stringValue\n }.joined(separator: \" \")\n let detail = providerErrors?.isEmpty == false ? \"\\(summary) \\(providerErrors ?? \"\")\" : summary\n throw AgentAPIError.requestFailed(detail)\n }\n return transcript\n }\n\n func autonomousStatus() async throws -> JSONValue {\n try await get(\"autonomous/status?limit=10\")\n }\n\n func janusStatus(limit: Int = 20) async throws -> JanusStatusResponse {\n try await get(\"janus/status?limit=\\(limit)\")\n }\n\n func janusPlannerContext(request: String = \"\") async throws -> JanusPlannerContextPreview {\n let encoded = request.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? \"\"\n return try await get(\"janus/planner-context?request=\\(encoded)\")\n }\n\n func collectorStatus(limit: Int = 10) async throws -> CollectorStatusResponse {\n try await get(\"collectors/status?limit=\\(limit)\")\n }\n\n func declareJanusTaskContext(_ draft: JanusTaskContextDraft) async throws -> JSONValue {\n var payload: [String: Any] = [\n \"goal\": draft.goal,\n \"summary\": draft.summary,\n \"privacy_mode\": draft.privacyMode,\n \"allowed_help\": draft.allowedHelpList,\n \"source\": \"macos_app\"\n ]\n removeEmptyStrings(from: &payload)\n return try await post(\"janus/task-contexts\", body: payload)\n }\n\n func createJanusMutedScope(_ draft: JanusMutedScopeDraft) async throws -> JSONValue {\n var payload: [String: Any] = [\n \"mode\": draft.mode,\n \"scope_type\": draft.scopeType,\n \"entity_refs\": draft.entityRefList,\n \"collector\": draft.collector,\n \"source\": draft.source,\n \"stimulus_type\": draft.stimulusType,\n \"expires_at\": draft.expiresAt,\n \"reason\": draft.reason\n ]\n removeEmptyStrings(from: &payload)\n return try await post(\"janus/muted-scopes\", body: payload)\n }\n\n func approveJanusDeepDive(requestID: String, reason: String) async throws -> JSONValue {\n try await post(\n \"janus/deep-dives/approve\",\n body: [\n \"request_id\": requestID,\n \"reason\": reason\n ]\n )\n }\n\n func rejectJanusDeepDive(requestID: String, reason: String) async throws -> JSONValue {\n try await post(\n \"janus/deep-dives/reject\",\n body: [\n \"request_id\": requestID,\n \"reason\": reason\n ]\n )\n }\n\n func recordJanusCorrection(\n correctionType: String,\n targetType: String,\n targetID: String,\n note: String,\n taskContext: JanusTaskContextDraft? = nil,\n mutedScope: JanusMutedScopeDraft? = nil\n ) async throws -> JSONValue {\n var payload: [String: Any] = [\n \"correction_type\": correctionType,\n \"target_type\": targetType,\n \"target_id\": targetID,\n \"note\": note\n ]\n if let taskContext, taskContext.hasContext {\n payload[\"goal\"] = taskContext.goal\n payload[\"summary\"] = taskContext.summary\n payload[\"privacy_mode\"] = taskContext.privacyMode\n payload[\"allowed_help\"] = taskContext.allowedHelpList\n }\n if let mutedScope, mutedScope.hasScope {\n payload[\"mode\"] = mutedScope.mode\n payload[\"scope_type\"] = mutedScope.scopeType\n payload[\"entity_refs\"] = mutedScope.entityRefList\n payload[\"collector\"] = mutedScope.collector\n payload[\"source\"] = mutedScope.source\n payload[\"stimulus_type\"] = mutedScope.stimulusType\n payload[\"expires_at\"] = mutedScope.expiresAt\n }\n removeEmptyStrings(from: &payload)\n return try await post(\n \"janus/corrections\",\n body: payload\n )\n }\n\n func cancelJanusMutedScope(scopeID: String, reason: String) async throws -> JSONValue {\n try await post(\n \"janus/muted-scopes/cancel\",\n body: [\n \"scope_id\": scopeID,\n \"reason\": reason\n ]\n )\n }\n\n func runAutonomousCycle(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"max_cycles\"] = settings.maxCycles\n payload[\"idle_sleep_seconds\"] = 0\n payload[\"stop_after_idle_cycles\"] = 1\n payload[\"allow_initiative\"] = settings.allowInitiative\n return try await post(\"autonomous/cycles\", body: payload)\n }\n\n func sendStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> AgentRunResponse {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"text\"] = formattedRequestText(text)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": \"detailed\"\n ]\n return try await post(\"stimuli\", body: payload)\n }\n\n func streamStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) throws -> AsyncThrowingStream<AgentStreamEvent, Error> {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"text\"] = formattedRequestText(text)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": \"detailed\"\n ]\n\n var request = URLRequest(url: try routeURL(\"stimuli/stream\"))\n request.httpMethod = \"POST\"\n request.timeoutInterval = 300\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: payload, options: [])\n\n return AsyncThrowingStream { continuation in\n let streamTask = Task {\n do {\n let (bytes, response) = try await URLSession.shared.bytes(for: request)\n if let http = response as? HTTPURLResponse, !(200..<300).contains(http.statusCode) {\n throw AgentAPIError.requestFailed(\"stimuli/stream failed with HTTP \\(http.statusCode).\")\n }\n var parser = ServerSentEventParser()\n for try await line in bytes.lines {\n if Task.isCancelled { break }\n if let event = try parser.consume(line) {\n continuation.yield(event)\n }\n }\n if Task.isCancelled {\n continuation.finish(throwing: CancellationError())\n return\n }\n if let event = try parser.finish() {\n continuation.yield(event)\n }\n continuation.finish()\n } catch {\n continuation.finish(throwing: error)\n }\n }\n continuation.onTermination = { _ in\n streamTask.cancel()\n }\n }\n }\n\n func approve(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/approve\", body: [\"note\": note])\n }\n\n func reject(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/reject\", body: [\"note\": note])\n }\n\n func cancelRun(_ runID: String, reason: String) async throws -> JSONValue {\n try await post(\"runs/\\(runID)/cancel\", body: [\"reason\": reason])\n }\n\n func runTimeline(_ runID: String) async throws -> JSONValue {\n try await get(\"runs/\\(runID)/timeline?limit=100\")\n }\n\n private func get<T: Decodable>(_ route: String) async throws -> T {\n let request = URLRequest(url: try routeURL(route))\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func post<T: Decodable>(_ route: String, body: [String: Any]) async throws -> T {\n var request = URLRequest(url: try routeURL(route))\n request.httpMethod = \"POST\"\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: body, options: [])\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func routeURL(_ route: String) throws -> URL {\n guard let url = URL(string: route, relativeTo: baseURL)?.absoluteURL else {\n throw AgentAPIError.requestFailed(\"Invalid API route: \\(route)\")\n }\n return url\n }\n\n private func validate(response: URLResponse, data: Data) throws {\n guard let http = response as? HTTPURLResponse, (200..<300).contains(http.statusCode) else {\n let message = String(data: data, encoding: .utf8) ?? \"Request failed.\"\n throw AgentAPIError.requestFailed(message)\n }\n }\n\n private func formattedRequestText(_ text: String) -> String {\n let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)\n guard !trimmed.isEmpty else { return text }\n return \"\"\"\n \\(trimmed)\n\n Response formatting requirements:\n \\(responseFormattingInstruction)\n \"\"\"\n }\n\n private func runtimePayload(settings: AppSettings, secrets: RuntimeSecrets) -> [String: Any] {\n var payload: [String: Any] = [\n \"planner\": settings.planner,\n \"approve_high_risk\": settings.approveHighRisk,\n \"model_provider\": apiModelProvider(settings.modelProvider),\n \"model\": settings.modelName,\n \"model_api_key_env\": modelKeyName(settings.modelProvider)\n ]\n if !settings.modelBaseURL.isEmpty {\n payload[\"model_base_url\"] = settings.modelBaseURL\n }\n let activeProvider = settings.janusModelProvider.trimmingCharacters(in: .whitespacesAndNewlines)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n payload[\"janus_model_provider\"] = apiModelProvider(activeProvider)\n payload[\"janus_model_api_key_env\"] = modelKeyName(activeProvider)\n if !settings.janusModelName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n payload[\"janus_model\"] = settings.janusModelName\n }\n if !settings.janusModelBaseURL.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n payload[\"janus_model_base_url\"] = settings.janusModelBaseURL\n }\n }\n\n var secretPayload: [String: String] = [:]\n addSecret(&secretPayload, name: modelKeyName(settings.modelProvider), value: secrets.modelAPIKey)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n addSecret(&secretPayload, name: modelKeyName(activeProvider), value: secrets.janusModelAPIKey)\n }\n addSecret(&secretPayload, name: \"DEEPGRAM_API_KEY\", value: secrets.deepgramAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_API_KEY\", value: secrets.elevenLabsAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_VOICE_ID\", value: settings.voiceId)\n addSecret(&secretPayload, name: \"ELEVENLABS_MODEL_ID\", value: settings.elevenLabsModel)\n for channel in settings.channels {\n addSecret(&secretPayload, name: channel.secretName, value: channel.secretValue)\n for item in channel.secretValues {\n addSecret(&secretPayload, name: item.key, value: item.value)\n }\n }\n if !secretPayload.isEmpty {\n payload[\"runtime_secrets\"] = secretPayload\n }\n return payload\n }\n\n private func addSecret(_ secrets: inout [String: String], name: String, value: String) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n let cleanValue = value.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, !cleanValue.isEmpty {\n secrets[cleanName] = cleanValue\n }\n }\n\n private func modelKeyName(_ provider: String) -> String {\n switch provider {\n case \"openrouter\": \"OPENROUTER_API_KEY\"\n case \"nous\": \"NOUS_API_KEY\"\n case \"novita\": \"NOVITA_API_KEY\"\n case \"lmstudio\": \"LM_API_KEY\"\n case \"anthropic\": \"ANTHROPIC_API_KEY\"\n case \"alibaba\": \"DASHSCOPE_API_KEY\"\n case \"groq\": \"GROQ_API_KEY\"\n case \"grok\", \"xai\": \"XAI_API_KEY\"\n case \"gemini\": \"GOOGLE_API_KEY\"\n case \"deepseek\": \"DEEPSEEK_API_KEY\"\n case \"mistral\": \"MISTRAL_API_KEY\"\n case \"cerebras\": \"CEREBRAS_API_KEY\"\n case \"ollama\": \"OLLAMA_API_KEY\"\n case \"ollama-cloud\": \"OLLAMA_API_KEY\"\n case \"local-openai\": \"LOCAL_LLM_API_KEY\"\n case \"vercel\": \"AI_GATEWAY_API_KEY\"\n case \"litellm\": \"LITELLM_API_KEY\"\n case \"nvidia\": \"NVIDIA_API_KEY\"\n case \"huggingface\": \"HF_TOKEN\"\n case \"zai\": \"GLM_API_KEY\"\n case \"kimi-coding\": \"KIMI_API_KEY\"\n case \"kimi-coding-cn\": \"KIMI_CN_API_KEY\"\n case \"stepfun\": \"STEPFUN_API_KEY\"\n case \"minimax\": \"MINIMAX_API_KEY\"\n case \"minimax-cn\": \"MINIMAX_CN_API_KEY\"\n case \"arcee\": \"ARCEEAI_API_KEY\"\n case \"gmi\": \"GMI_API_KEY\"\n case \"xiaomi\": \"XIAOMI_API_KEY\"\n case \"tencent-tokenhub\": \"TOKENHUB_API_KEY\"\n case \"opencode-zen\": \"OPENCODE_ZEN_API_KEY\"\n case \"opencode-go\": \"OPENCODE_GO_API_KEY\"\n case \"kilocode\": \"KILOCODE_API_KEY\"\n case \"azure-openai\": \"AZURE_OPENAI_API_KEY\"\n case \"azure-foundry\": \"AZURE_FOUNDRY_API_KEY\"\n case \"copilot\", \"copilot-acp\": \"GITHUB_TOKEN\"\n case \"bedrock\": \"AWS_ACCESS_KEY_ID\"\n case \"browser-use-cloud\": \"BROWSER_USE_API_KEY\"\n default: \"OPENAI_API_KEY\"\n }\n }\n\n private func apiModelProvider(_ provider: String) -> String {\n provider == \"openai\" ? \"openai-responses\" : provider\n }\n\n private func channelSecretRefs(_ setup: ChannelSetup) -> [String: String] {\n var refs: [String: String] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n refs[\"primary\"] = primary\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n refs[cleanName] = cleanName\n }\n }\n return refs\n }\n\n private func channelSecretConfigured(_ setup: ChannelSetup) -> [String: Bool] {\n var configured: [String: Bool] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n configured[\"primary\"] = setup.secretConfigured || !setup.secretValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n configured[cleanName] = !setup.secretValues[cleanName, default: \"\"].trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n }\n return configured\n }\n\n private func cleanList(_ values: [String]) -> [String] {\n values.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }.filter { !$0.isEmpty }\n }\n\n private func removeEmptyStrings(from payload: inout [String: Any]) {\n for (key, value) in payload {\n if let string = value as? String, string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n payload.removeValue(forKey: key)\n }\n if let strings = value as? [String], strings.isEmpty {\n payload.removeValue(forKey: key)\n }\n }\n }\n\n private static func normalizedBaseURL(_ value: String, fallback: URL = URL(string: \"http://127.0.0.1:8765/\")!) -> URL {\n let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)\n .trimmingCharacters(in: CharacterSet(charactersIn: \"/\"))\n guard !trimmed.isEmpty, let url = URL(string: \"\\(trimmed)/\") else {\n return fallback\n }\n return url\n }\n\n private static func urlEncode(_ value: String) -> String {\n value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? value\n }\n}\n\nenum AgentAPIError: LocalizedError {\n case requestFailed(String)\n\n var errorDescription: String? {\n switch self {\n case .requestFailed(let message): message\n }\n }\n}\n\nprivate struct ServerSentEventParser {\n private var eventName = \"message\"\n private var dataLines: [String] = []\n private let decoder = JSONDecoder()\n\n mutating func consume(_ line: String) throws -> AgentStreamEvent? {\n if line.hasPrefix(\"event: \") {\n eventName = String(line.dropFirst(\"event: \".count)).trimmingCharacters(in: .whitespaces)\n return nil\n }\n if line.hasPrefix(\"data: \") {\n dataLines.append(String(line.dropFirst(\"data: \".count)))\n return try finish()\n }\n if line.isEmpty {\n return try finish()\n }\n return nil\n }\n\n mutating func finish() throws -> AgentStreamEvent? {\n guard !dataLines.isEmpty else { return nil }\n let data = Data(dataLines.joined(separator: \"\\n\").utf8)\n let value = try decoder.decode(JSONValue.self, from: data)\n let event = AgentStreamEvent(event: eventName, data: value)\n eventName = \"message\"\n dataLines = []\n return event\n }\n}\n"}, "5272e1d0a3d25da8abf9dfe0b0dfbf99779f9c00": {"t": 1785756644.3369555, "n": 28004, "h": "4ca9eaef6051765f", "c": "import Foundation\n\nstruct RuntimeSecrets {\n var modelAPIKey = \"\"\n var janusModelAPIKey = \"\"\n var deepgramAPIKey = \"\"\n var elevenLabsAPIKey = \"\"\n}\n\n@MainActor\nfinal class AgentAPIClient {\n private var baseURL: URL\n private let decoder: JSONDecoder\n private let encoder = JSONEncoder()\n private let responseFormattingInstruction = \"\"\"\n Respond with a detailed final answer in GitHub-flavored Markdown. Use clear sections, bullets, and tables where they make the answer easier to scan. Include caveats, source/tool evidence, and next steps when relevant. Do not return a single unformatted paragraph unless the user explicitly asks for a very short answer.\n \"\"\"\n\n init(baseURL: String) {\n self.baseURL = AgentAPIClient.normalizedBaseURL(baseURL)\n decoder = JSONDecoder()\n }\n\n func setBaseURL(_ value: String) {\n baseURL = AgentAPIClient.normalizedBaseURL(value, fallback: baseURL)\n }\n\n func health() async throws -> HealthPayload {\n try await get(\"health\")\n }\n\n func systemStatus() async throws -> JSONValue {\n try await get(\"system/status\")\n }\n\n func latestUpdate() async throws -> UpdateInfo {\n try await get(\"updates/latest?platform=macos\")\n }\n\n func tools() async throws -> ToolCatalog {\n try await get(\"tools\")\n }\n\n func modelProviders() async throws -> ModelProviderCatalog {\n try await get(\"model/providers\")\n }\n\n func runs() async throws -> [RunItem] {\n try await get(\"runs?limit=30\")\n }\n\n func approvals() async throws -> [ApprovalItem] {\n try await get(\"approvals?status=pending&limit=30\")\n }\n\n func channels() async throws -> [ChannelInfo] {\n try await get(\"channels\")\n }\n\n func connectors() async throws -> ConnectorCatalog {\n try await get(\"connectors\")\n }\n\n func configureConnector(providerID: String, clientID: String, clientSecret: String, redirectURI: String) async throws -> JSONValue {\n try await post(\n \"connectors/configure\",\n body: [\n \"provider_id\": providerID,\n \"client_id\": clientID,\n \"client_secret\": clientSecret,\n \"redirect_uri\": redirectURI\n ]\n )\n }\n\n func prepareConnector(providerID: String) async throws -> ConnectorAuthorization {\n try await post(\"connectors/connect\", body: [\"provider_id\": providerID])\n }\n\n func refreshConnector(providerID: String) async throws -> JSONValue {\n try await post(\"connectors/refresh\", body: [\"provider_id\": providerID])\n }\n\n func disconnectConnector(providerID: String) async throws -> JSONValue {\n try await post(\"connectors/disconnect\", body: [\"provider_id\": providerID])\n }\n\n func outbox() async throws -> OutboxEnvelope {\n try await get(\"channels/outbox\")\n }\n\n func channelStatus(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/status\", body: payload)\n }\n\n func channelDoctor(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/doctor\", body: payload)\n }\n\n func channelRequirements(channelID: String) async throws -> JSONValue {\n try await get(\"channels/requirements?channel_id=\\(Self.urlEncode(channelID))\")\n }\n\n func runChannelSmoke(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"prepare_messages\"] = true\n payload[\"dry_run_sends\"] = true\n return try await post(\"channels/smoke\", body: payload)\n }\n\n func channelListeners(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/listeners\", body: payload)\n }\n\n func tickChannelListener(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func tickAllChannelListeners(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func saveChannelSetup(channel: ChannelInfo, setup: ChannelSetup) async throws -> JSONValue {\n let payload: [String: Any] = [\n \"channel_id\": channel.channelId,\n \"enabled\": setup.enabled,\n \"listen_enabled\": setup.listenEnabled,\n \"conversation_defaults\": [\n \"conversation_id\": setup.conversationId,\n \"conversation_type\": setup.conversationType\n ],\n \"secret_refs\": channelSecretRefs(setup),\n \"secret_configured\": channelSecretConfigured(setup),\n \"allowlist\": cleanList(setup.allowlist),\n \"group_allowlist\": cleanList(setup.groupAllowlist),\n \"notes\": setup.notes\n ]\n return try await post(\"channels/setup\", body: payload)\n }\n\n func prepareChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Prepared from the macOS app channel panel.\"\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/prepare\", body: payload)\n }\n\n func sendChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Approval-gated send from the macOS app channel panel.\"\n payload[\"approve_high_risk\"] = settings.approveHighRisk\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/send\", body: payload)\n }\n\n func sendChannelInbound(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"conversation_type\"] = setup.conversationType.isEmpty ? \"dm\" : setup.conversationType\n payload[\"sender_id\"] = \"macos-app\"\n payload[\"text\"] = text\n payload[\"requires_response\"] = true\n payload[\"prepare_reply\"] = true\n return try await post(\"channels/inbound\", body: payload)\n }\n\n func voiceStatus(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n try await post(\"voice/status\", body: runtimePayload(settings: settings, secrets: secrets))\n }\n\n func transcribeAudio(_ audioURL: URL, settings: AppSettings, secrets: RuntimeSecrets) async throws -> String {\n let audioData = try Data(contentsOf: audioURL)\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"audio_base64\"] = audioData.base64EncodedString()\n payload[\"filename\"] = audioURL.lastPathComponent\n payload[\"provider\"] = secrets.deepgramAPIKey.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? \"local-whisper\" : \"deepgram\"\n payload[\"smart_format\"] = true\n payload[\"mime_type\"] = \"audio/mp4\"\n payload[\"reason\"] = \"macOS wake listener captured the user's post-wake task.\"\n let result: JSONValue = try await post(\"voice/transcribe\", body: payload)\n let transcript = result[\"transcript\"]?.stringValue?.trimmingCharacters(in: .whitespacesAndNewlines) ?? \"\"\n guard !transcript.isEmpty else {\n let summary = result[\"summary\"]?.stringValue ?? \"No transcript was returned.\"\n let providerErrors = result[\"provider_errors\"]?.arrayValue?.compactMap { item in\n item[\"error\"]?.stringValue\n }.joined(separator: \" \")\n let detail = providerErrors?.isEmpty == false ? \"\\(summary) \\(providerErrors ?? \"\")\" : summary\n throw AgentAPIError.requestFailed(detail)\n }\n return transcript\n }\n\n func autonomousStatus() async throws -> JSONValue {\n try await get(\"autonomous/status?limit=10\")\n }\n\n func janusStatus(limit: Int = 20) async throws -> JanusStatusResponse {\n try await get(\"janus/status?limit=\\(limit)\")\n }\n\n func janusPlannerContext(request: String = \"\") async throws -> JanusPlannerContextPreview {\n let encoded = request.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? \"\"\n return try await get(\"janus/planner-context?request=\\(encoded)\")\n }\n\n func collectorStatus(limit: Int = 10) async throws -> CollectorStatusResponse {\n try await get(\"collectors/status?limit=\\(limit)\")\n }\n\n func declareJanusTaskContext(_ draft: JanusTaskContextDraft) async throws -> JSONValue {\n var payload: [String: Any] = [\n \"goal\": draft.goal,\n \"summary\": draft.summary,\n \"privacy_mode\": draft.privacyMode,\n \"allowed_help\": draft.allowedHelpList,\n \"source\": \"macos_app\"\n ]\n removeEmptyStrings(from: &payload)\n return try await post(\"janus/task-contexts\", body: payload)\n }\n\n func createJanusMutedScope(_ draft: JanusMutedScopeDraft) async throws -> JSONValue {\n var payload: [String: Any] = [\n \"mode\": draft.mode,\n \"scope_type\": draft.scopeType,\n \"entity_refs\": draft.entityRefList,\n \"collector\": draft.collector,\n \"source\": draft.source,\n \"stimulus_type\": draft.stimulusType,\n \"expires_at\": draft.expiresAt,\n \"reason\": draft.reason\n ]\n removeEmptyStrings(from: &payload)\n return try await post(\"janus/muted-scopes\", body: payload)\n }\n\n func approveJanusDeepDive(requestID: String, reason: String) async throws -> JSONValue {\n try await post(\n \"janus/deep-dives/approve\",\n body: [\n \"request_id\": requestID,\n \"reason\": reason\n ]\n )\n }\n\n func rejectJanusDeepDive(requestID: String, reason: String) async throws -> JSONValue {\n try await post(\n \"janus/deep-dives/reject\",\n body: [\n \"request_id\": requestID,\n \"reason\": reason\n ]\n )\n }\n\n func recordJanusCorrection(\n correctionType: String,\n targetType: String,\n targetID: String,\n note: String,\n taskContext: JanusTaskContextDraft? = nil,\n mutedScope: JanusMutedScopeDraft? = nil\n ) async throws -> JSONValue {\n var payload: [String: Any] = [\n \"correction_type\": correctionType,\n \"target_type\": targetType,\n \"target_id\": targetID,\n \"note\": note\n ]\n if let taskContext, taskContext.hasContext {\n payload[\"goal\"] = taskContext.goal\n payload[\"summary\"] = taskContext.summary\n payload[\"privacy_mode\"] = taskContext.privacyMode\n payload[\"allowed_help\"] = taskContext.allowedHelpList\n }\n if let mutedScope, mutedScope.hasScope {\n payload[\"mode\"] = mutedScope.mode\n payload[\"scope_type\"] = mutedScope.scopeType\n payload[\"entity_refs\"] = mutedScope.entityRefList\n payload[\"collector\"] = mutedScope.collector\n payload[\"source\"] = mutedScope.source\n payload[\"stimulus_type\"] = mutedScope.stimulusType\n payload[\"expires_at\"] = mutedScope.expiresAt\n }\n removeEmptyStrings(from: &payload)\n return try await post(\n \"janus/corrections\",\n body: payload\n )\n }\n\n func cancelJanusMutedScope(scopeID: String, reason: String) async throws -> JSONValue {\n try await post(\n \"janus/muted-scopes/cancel\",\n body: [\n \"scope_id\": scopeID,\n \"reason\": reason\n ]\n )\n }\n\n func runAutonomousCycle(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"max_cycles\"] = settings.maxCycles\n payload[\"idle_sleep_seconds\"] = 0\n payload[\"stop_after_idle_cycles\"] = 1\n payload[\"allow_initiative\"] = settings.allowInitiative\n return try await post(\"autonomous/cycles\", body: payload)\n }\n\n func sendStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> AgentRunResponse {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"text\"] = formattedRequestText(text)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": \"detailed\"\n ]\n return try await post(\"stimuli\", body: payload)\n }\n\n func streamStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) throws -> AsyncThrowingStream<AgentStreamEvent, Error> {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"text\"] = formattedRequestText(text)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": \"detailed\"\n ]\n\n var request = URLRequest(url: try routeURL(\"stimuli/stream\"))\n request.httpMethod = \"POST\"\n request.timeoutInterval = 300\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: payload, options: [])\n\n return AsyncThrowingStream { continuation in\n let streamTask = Task {\n do {\n let (bytes, response) = try await URLSession.shared.bytes(for: request)\n if let http = response as? HTTPURLResponse, !(200..<300).contains(http.statusCode) {\n throw AgentAPIError.requestFailed(\"stimuli/stream failed with HTTP \\(http.statusCode).\")\n }\n var parser = ServerSentEventParser()\n for try await line in bytes.lines {\n if Task.isCancelled { break }\n if let event = try parser.consume(line) {\n continuation.yield(event)\n }\n }\n if Task.isCancelled {\n continuation.finish(throwing: CancellationError())\n return\n }\n if let event = try parser.finish() {\n continuation.yield(event)\n }\n continuation.finish()\n } catch {\n continuation.finish(throwing: error)\n }\n }\n continuation.onTermination = { _ in\n streamTask.cancel()\n }\n }\n }\n\n func approve(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/approve\", body: [\"note\": note])\n }\n\n func reject(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/reject\", body: [\"note\": note])\n }\n\n func cancelRun(_ runID: String, reason: String) async throws -> JSONValue {\n try await post(\"runs/\\(runID)/cancel\", body: [\"reason\": reason])\n }\n\n func runTimeline(_ runID: String) async throws -> JSONValue {\n try await get(\"runs/\\(runID)/timeline?limit=100\")\n }\n\n private func get<T: Decodable>(_ route: String) async throws -> T {\n let request = URLRequest(url: try routeURL(route))\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func post<T: Decodable>(_ route: String, body: [String: Any]) async throws -> T {\n var request = URLRequest(url: try routeURL(route))\n request.httpMethod = \"POST\"\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: body, options: [])\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func routeURL(_ route: String) throws -> URL {\n guard let url = URL(string: route, relativeTo: baseURL)?.absoluteURL else {\n throw AgentAPIError.requestFailed(\"Invalid API route: \\(route)\")\n }\n return url\n }\n\n private func validate(response: URLResponse, data: Data) throws {\n guard let http = response as? HTTPURLResponse, (200..<300).contains(http.statusCode) else {\n let message = String(data: data, encoding: .utf8) ?? \"Request failed.\"\n throw AgentAPIError.requestFailed(message)\n }\n }\n\n private func formattedRequestText(_ text: String) -> String {\n let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)\n guard !trimmed.isEmpty else { return text }\n return \"\"\"\n \\(trimmed)\n\n Response formatting requirements:\n \\(responseFormattingInstruction)\n \"\"\"\n }\n\n private func runtimePayload(settings: AppSettings, secrets: RuntimeSecrets) -> [String: Any] {\n var payload: [String: Any] = [\n \"planner\": settings.planner,\n \"approve_high_risk\": settings.approveHighRisk,\n \"model_provider\": apiModelProvider(settings.modelProvider),\n \"model\": settings.modelName,\n \"model_api_key_env\": modelKeyName(settings.modelProvider)\n ]\n if !settings.modelBaseURL.isEmpty {\n payload[\"model_base_url\"] = settings.modelBaseURL\n }\n let activeProvider = settings.janusModelProvider.trimmingCharacters(in: .whitespacesAndNewlines)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n payload[\"janus_model_provider\"] = apiModelProvider(activeProvider)\n payload[\"janus_model_api_key_env\"] = modelKeyName(activeProvider)\n if !settings.janusModelName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n payload[\"janus_model\"] = settings.janusModelName\n }\n if !settings.janusModelBaseURL.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n payload[\"janus_model_base_url\"] = settings.janusModelBaseURL\n }\n }\n\n var secretPayload: [String: String] = [:]\n addSecret(&secretPayload, name: modelKeyName(settings.modelProvider), value: secrets.modelAPIKey)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n addSecret(&secretPayload, name: modelKeyName(activeProvider), value: secrets.janusModelAPIKey)\n }\n addSecret(&secretPayload, name: \"DEEPGRAM_API_KEY\", value: secrets.deepgramAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_API_KEY\", value: secrets.elevenLabsAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_VOICE_ID\", value: settings.voiceId)\n addSecret(&secretPayload, name: \"ELEVENLABS_MODEL_ID\", value: settings.elevenLabsModel)\n for channel in settings.channels {\n addSecret(&secretPayload, name: channel.secretName, value: channel.secretValue)\n for item in channel.secretValues {\n addSecret(&secretPayload, name: item.key, value: item.value)\n }\n }\n if !secretPayload.isEmpty {\n payload[\"runtime_secrets\"] = secretPayload\n }\n return payload\n }\n\n private func addSecret(_ secrets: inout [String: String], name: String, value: String) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n let cleanValue = value.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, !cleanValue.isEmpty {\n secrets[cleanName] = cleanValue\n }\n }\n\n private func modelKeyName(_ provider: String) -> String {\n switch provider {\n case \"openrouter\": \"OPENROUTER_API_KEY\"\n case \"nous\": \"NOUS_API_KEY\"\n case \"novita\": \"NOVITA_API_KEY\"\n case \"lmstudio\": \"LM_API_KEY\"\n case \"anthropic\": \"ANTHROPIC_API_KEY\"\n case \"alibaba\": \"DASHSCOPE_API_KEY\"\n case \"groq\": \"GROQ_API_KEY\"\n case \"grok\", \"xai\": \"XAI_API_KEY\"\n case \"gemini\": \"GOOGLE_API_KEY\"\n case \"deepseek\": \"DEEPSEEK_API_KEY\"\n case \"mistral\": \"MISTRAL_API_KEY\"\n case \"cerebras\": \"CEREBRAS_API_KEY\"\n case \"ollama\": \"OLLAMA_API_KEY\"\n case \"ollama-cloud\": \"OLLAMA_API_KEY\"\n case \"local-openai\": \"LOCAL_LLM_API_KEY\"\n case \"vercel\": \"AI_GATEWAY_API_KEY\"\n case \"litellm\": \"LITELLM_API_KEY\"\n case \"nvidia\": \"NVIDIA_API_KEY\"\n case \"huggingface\": \"HF_TOKEN\"\n case \"zai\": \"GLM_API_KEY\"\n case \"kimi-coding\": \"KIMI_API_KEY\"\n case \"kimi-coding-cn\": \"KIMI_CN_API_KEY\"\n case \"stepfun\": \"STEPFUN_API_KEY\"\n case \"minimax\": \"MINIMAX_API_KEY\"\n case \"minimax-cn\": \"MINIMAX_CN_API_KEY\"\n case \"arcee\": \"ARCEEAI_API_KEY\"\n case \"gmi\": \"GMI_API_KEY\"\n case \"xiaomi\": \"XIAOMI_API_KEY\"\n case \"tencent-tokenhub\": \"TOKENHUB_API_KEY\"\n case \"opencode-zen\": \"OPENCODE_ZEN_API_KEY\"\n case \"opencode-go\": \"OPENCODE_GO_API_KEY\"\n case \"kilocode\": \"KILOCODE_API_KEY\"\n case \"azure-openai\": \"AZURE_OPENAI_API_KEY\"\n case \"azure-foundry\": \"AZURE_FOUNDRY_API_KEY\"\n case \"copilot\", \"copilot-acp\": \"GITHUB_TOKEN\"\n case \"bedrock\": \"AWS_ACCESS_KEY_ID\"\n case \"browser-use-cloud\": \"BROWSER_USE_API_KEY\"\n default: \"OPENAI_API_KEY\"\n }\n }\n\n private func apiModelProvider(_ provider: String) -> String {\n provider == \"openai\" ? \"openai-responses\" : provider\n }\n\n private func channelSecretRefs(_ setup: ChannelSetup) -> [String: String] {\n var refs: [String: String] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n refs[\"primary\"] = primary\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n refs[cleanName] = cleanName\n }\n }\n return refs\n }\n\n private func channelSecretConfigured(_ setup: ChannelSetup) -> [String: Bool] {\n var configured: [String: Bool] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n configured[\"primary\"] = setup.secretConfigured || !setup.secretValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n configured[cleanName] = !setup.secretValues[cleanName, default: \"\"].trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n }\n return configured\n }\n\n private func cleanList(_ values: [String]) -> [String] {\n values.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }.filter { !$0.isEmpty }\n }\n\n private func removeEmptyStrings(from payload: inout [String: Any]) {\n for (key, value) in payload {\n if let string = value as? String, string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n payload.removeValue(forKey: key)\n }\n if let strings = value as? [String], strings.isEmpty {\n payload.removeValue(forKey: key)\n }\n }\n }\n\n private static func normalizedBaseURL(_ value: String, fallback: URL = URL(string: \"http://127.0.0.1:8765/\")!) -> URL {\n let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)\n .trimmingCharacters(in: CharacterSet(charactersIn: \"/\"))\n guard !trimmed.isEmpty, let url = URL(string: \"\\(trimmed)/\") else {\n return fallback\n }\n return url\n }\n\n private static func urlEncode(_ value: String) -> String {\n value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? value\n }\n}\n\nenum AgentAPIError: LocalizedError {\n case requestFailed(String)\n\n var errorDescription: String? {\n switch self {\n case .requestFailed(let message): message\n }\n }\n}\n\nprivate struct ServerSentEventParser {\n private var eventName = \"message\"\n private var dataLines: [String] = []\n private let decoder = JSONDecoder()\n\n mutating func consume(_ line: String) throws -> AgentStreamEvent? {\n if line.hasPrefix(\"event: \") {\n eventName = String(line.dropFirst(\"event: \".count)).trimmingCharacters(in: .whitespaces)\n return nil\n }\n if line.hasPrefix(\"data: \") {\n dataLines.append(String(line.dropFirst(\"data: \".count)))\n return try finish()\n }\n if line.isEmpty {\n return try finish()\n }\n return nil\n }\n\n mutating func finish() throws -> AgentStreamEvent? {\n guard !dataLines.isEmpty else { return nil }\n let data = Data(dataLines.joined(separator: \"\\n\").utf8)\n let value = try decoder.decode(JSONValue.self, from: data)\n let event = AgentStreamEvent(event: eventName, data: value)\n eventName = \"message\"\n dataLines = []\n return event\n }\n}\n"}, "8d082d4caf3cfac1a7f0f329ec876366f53df68c": {"t": 1785756645.8739579, "n": 28155, "h": "23e8e6a8e8a04514", "c": "import Foundation\n\nstruct RuntimeSecrets {\n var modelAPIKey = \"\"\n var activeModelAPIKey = \"\"\n var deepgramAPIKey = \"\"\n var elevenLabsAPIKey = \"\"\n}\n\n@MainActor\nfinal class AgentAPIClient {\n private var baseURL: URL\n private let decoder: JSONDecoder\n private let encoder = JSONEncoder()\n private let responseFormattingInstruction = \"\"\"\n Respond with a detailed final answer in GitHub-flavored Markdown. Use clear sections, bullets, and tables where they make the answer easier to scan. Include caveats, source/tool evidence, and next steps when relevant. Do not return a single unformatted paragraph unless the user explicitly asks for a very short answer.\n \"\"\"\n\n init(baseURL: String) {\n self.baseURL = AgentAPIClient.normalizedBaseURL(baseURL)\n decoder = JSONDecoder()\n }\n\n func setBaseURL(_ value: String) {\n baseURL = AgentAPIClient.normalizedBaseURL(value, fallback: baseURL)\n }\n\n func health() async throws -> HealthPayload {\n try await get(\"health\")\n }\n\n func systemStatus() async throws -> JSONValue {\n try await get(\"system/status\")\n }\n\n func latestUpdate() async throws -> UpdateInfo {\n try await get(\"updates/latest?platform=macos\")\n }\n\n func tools() async throws -> ToolCatalog {\n try await get(\"tools\")\n }\n\n func modelProviders() async throws -> ModelProviderCatalog {\n try await get(\"model/providers\")\n }\n\n func runs() async throws -> [RunItem] {\n try await get(\"runs?limit=30\")\n }\n\n func approvals() async throws -> [ApprovalItem] {\n try await get(\"approvals?status=pending&limit=30\")\n }\n\n func channels() async throws -> [ChannelInfo] {\n try await get(\"channels\")\n }\n\n func connectors() async throws -> ConnectorCatalog {\n try await get(\"connectors\")\n }\n\n func configureConnector(providerID: String, clientID: String, clientSecret: String, redirectURI: String) async throws -> JSONValue {\n try await post(\n \"connectors/configure\",\n body: [\n \"provider_id\": providerID,\n \"client_id\": clientID,\n \"client_secret\": clientSecret,\n \"redirect_uri\": redirectURI\n ]\n )\n }\n\n func prepareConnector(providerID: String) async throws -> ConnectorAuthorization {\n try await post(\"connectors/connect\", body: [\"provider_id\": providerID])\n }\n\n func refreshConnector(providerID: String) async throws -> JSONValue {\n try await post(\"connectors/refresh\", body: [\"provider_id\": providerID])\n }\n\n func disconnectConnector(providerID: String) async throws -> JSONValue {\n try await post(\"connectors/disconnect\", body: [\"provider_id\": providerID])\n }\n\n func outbox() async throws -> OutboxEnvelope {\n try await get(\"channels/outbox\")\n }\n\n func channelStatus(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/status\", body: payload)\n }\n\n func channelDoctor(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/doctor\", body: payload)\n }\n\n func channelRequirements(channelID: String) async throws -> JSONValue {\n try await get(\"channels/requirements?channel_id=\\(Self.urlEncode(channelID))\")\n }\n\n func runChannelSmoke(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"prepare_messages\"] = true\n payload[\"dry_run_sends\"] = true\n return try await post(\"channels/smoke\", body: payload)\n }\n\n func channelListeners(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/listeners\", body: payload)\n }\n\n func tickChannelListener(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func tickAllChannelListeners(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func saveChannelSetup(channel: ChannelInfo, setup: ChannelSetup) async throws -> JSONValue {\n let payload: [String: Any] = [\n \"channel_id\": channel.channelId,\n \"enabled\": setup.enabled,\n \"listen_enabled\": setup.listenEnabled,\n \"conversation_defaults\": [\n \"conversation_id\": setup.conversationId,\n \"conversation_type\": setup.conversationType\n ],\n \"secret_refs\": channelSecretRefs(setup),\n \"secret_configured\": channelSecretConfigured(setup),\n \"allowlist\": cleanList(setup.allowlist),\n \"group_allowlist\": cleanList(setup.groupAllowlist),\n \"notes\": setup.notes\n ]\n return try await post(\"channels/setup\", body: payload)\n }\n\n func prepareChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Prepared from the macOS app channel panel.\"\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/prepare\", body: payload)\n }\n\n func sendChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Approval-gated send from the macOS app channel panel.\"\n payload[\"approve_high_risk\"] = settings.approveHighRisk\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/send\", body: payload)\n }\n\n func sendChannelInbound(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"conversation_type\"] = setup.conversationType.isEmpty ? \"dm\" : setup.conversationType\n payload[\"sender_id\"] = \"macos-app\"\n payload[\"text\"] = text\n payload[\"requires_response\"] = true\n payload[\"prepare_reply\"] = true\n return try await post(\"channels/inbound\", body: payload)\n }\n\n func voiceStatus(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n try await post(\"voice/status\", body: runtimePayload(settings: settings, secrets: secrets))\n }\n\n func transcribeAudio(_ audioURL: URL, settings: AppSettings, secrets: RuntimeSecrets) async throws -> String {\n let audioData = try Data(contentsOf: audioURL)\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"audio_base64\"] = audioData.base64EncodedString()\n payload[\"filename\"] = audioURL.lastPathComponent\n payload[\"provider\"] = secrets.deepgramAPIKey.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? \"local-whisper\" : \"deepgram\"\n payload[\"smart_format\"] = true\n payload[\"mime_type\"] = \"audio/mp4\"\n payload[\"reason\"] = \"macOS wake listener captured the user's post-wake task.\"\n let result: JSONValue = try await post(\"voice/transcribe\", body: payload)\n let transcript = result[\"transcript\"]?.stringValue?.trimmingCharacters(in: .whitespacesAndNewlines) ?? \"\"\n guard !transcript.isEmpty else {\n let summary = result[\"summary\"]?.stringValue ?? \"No transcript was returned.\"\n let providerErrors = result[\"provider_errors\"]?.arrayValue?.compactMap { item in\n item[\"error\"]?.stringValue\n }.joined(separator: \" \")\n let detail = providerErrors?.isEmpty == false ? \"\\(summary) \\(providerErrors ?? \"\")\" : summary\n throw AgentAPIError.requestFailed(detail)\n }\n return transcript\n }\n\n func autonomousStatus() async throws -> JSONValue {\n try await get(\"autonomous/status?limit=10\")\n }\n\n func activeAgentStatus(limit: Int = 20) async throws -> ActiveAgentStatusResponse {\n try await get(\"active-agent/status?limit=\\(limit)\")\n }\n\n func activeAgentPlannerContext(request: String = \"\") async throws -> ActiveAgentPlannerContextPreview {\n let encoded = request.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? \"\"\n return try await get(\"active-agent/planner-context?request=\\(encoded)\")\n }\n\n func collectorStatus(limit: Int = 10) async throws -> CollectorStatusResponse {\n try await get(\"collectors/status?limit=\\(limit)\")\n }\n\n func declareActiveAgentTaskContext(_ draft: ActiveAgentTaskContextDraft) async throws -> JSONValue {\n var payload: [String: Any] = [\n \"goal\": draft.goal,\n \"summary\": draft.summary,\n \"privacy_mode\": draft.privacyMode,\n \"allowed_help\": draft.allowedHelpList,\n \"source\": \"macos_app\"\n ]\n removeEmptyStrings(from: &payload)\n return try await post(\"active-agent/task-contexts\", body: payload)\n }\n\n func createActiveAgentMutedScope(_ draft: ActiveAgentMutedScopeDraft) async throws -> JSONValue {\n var payload: [String: Any] = [\n \"mode\": draft.mode,\n \"scope_type\": draft.scopeType,\n \"entity_refs\": draft.entityRefList,\n \"collector\": draft.collector,\n \"source\": draft.source,\n \"stimulus_type\": draft.stimulusType,\n \"expires_at\": draft.expiresAt,\n \"reason\": draft.reason\n ]\n removeEmptyStrings(from: &payload)\n return try await post(\"active-agent/muted-scopes\", body: payload)\n }\n\n func approveActiveAgentDeepDive(requestID: String, reason: String) async throws -> JSONValue {\n try await post(\n \"active-agent/deep-dives/approve\",\n body: [\n \"request_id\": requestID,\n \"reason\": reason\n ]\n )\n }\n\n func rejectActiveAgentDeepDive(requestID: String, reason: String) async throws -> JSONValue {\n try await post(\n \"active-agent/deep-dives/reject\",\n body: [\n \"request_id\": requestID,\n \"reason\": reason\n ]\n )\n }\n\n func recordActiveAgentCorrection(\n correctionType: String,\n targetType: String,\n targetID: String,\n note: String,\n taskContext: ActiveAgentTaskContextDraft? = nil,\n mutedScope: ActiveAgentMutedScopeDraft? = nil\n ) async throws -> JSONValue {\n var payload: [String: Any] = [\n \"correction_type\": correctionType,\n \"target_type\": targetType,\n \"target_id\": targetID,\n \"note\": note\n ]\n if let taskContext, taskContext.hasContext {\n payload[\"goal\"] = taskContext.goal\n payload[\"summary\"] = taskContext.summary\n payload[\"privacy_mode\"] = taskContext.privacyMode\n payload[\"allowed_help\"] = taskContext.allowedHelpList\n }\n if let mutedScope, mutedScope.hasScope {\n payload[\"mode\"] = mutedScope.mode\n payload[\"scope_type\"] = mutedScope.scopeType\n payload[\"entity_refs\"] = mutedScope.entityRefList\n payload[\"collector\"] = mutedScope.collector\n payload[\"source\"] = mutedScope.source\n payload[\"stimulus_type\"] = mutedScope.stimulusType\n payload[\"expires_at\"] = mutedScope.expiresAt\n }\n removeEmptyStrings(from: &payload)\n return try await post(\n \"active-agent/corrections\",\n body: payload\n )\n }\n\n func cancelActiveAgentMutedScope(scopeID: String, reason: String) async throws -> JSONValue {\n try await post(\n \"active-agent/muted-scopes/cancel\",\n body: [\n \"scope_id\": scopeID,\n \"reason\": reason\n ]\n )\n }\n\n func runAutonomousCycle(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"max_cycles\"] = settings.maxCycles\n payload[\"idle_sleep_seconds\"] = 0\n payload[\"stop_after_idle_cycles\"] = 1\n payload[\"allow_initiative\"] = settings.allowInitiative\n return try await post(\"autonomous/cycles\", body: payload)\n }\n\n func sendStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> AgentRunResponse {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"text\"] = formattedRequestText(text)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": \"detailed\"\n ]\n return try await post(\"stimuli\", body: payload)\n }\n\n func streamStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) throws -> AsyncThrowingStream<AgentStreamEvent, Error> {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"text\"] = formattedRequestText(text)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": \"detailed\"\n ]\n\n var request = URLRequest(url: try routeURL(\"stimuli/stream\"))\n request.httpMethod = \"POST\"\n request.timeoutInterval = 300\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: payload, options: [])\n\n return AsyncThrowingStream { continuation in\n let streamTask = Task {\n do {\n let (bytes, response) = try await URLSession.shared.bytes(for: request)\n if let http = response as? HTTPURLResponse, !(200..<300).contains(http.statusCode) {\n throw AgentAPIError.requestFailed(\"stimuli/stream failed with HTTP \\(http.statusCode).\")\n }\n var parser = ServerSentEventParser()\n for try await line in bytes.lines {\n if Task.isCancelled { break }\n if let event = try parser.consume(line) {\n continuation.yield(event)\n }\n }\n if Task.isCancelled {\n continuation.finish(throwing: CancellationError())\n return\n }\n if let event = try parser.finish() {\n continuation.yield(event)\n }\n continuation.finish()\n } catch {\n continuation.finish(throwing: error)\n }\n }\n continuation.onTermination = { _ in\n streamTask.cancel()\n }\n }\n }\n\n func approve(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/approve\", body: [\"note\": note])\n }\n\n func reject(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/reject\", body: [\"note\": note])\n }\n\n func cancelRun(_ runID: String, reason: String) async throws -> JSONValue {\n try await post(\"runs/\\(runID)/cancel\", body: [\"reason\": reason])\n }\n\n func runTimeline(_ runID: String) async throws -> JSONValue {\n try await get(\"runs/\\(runID)/timeline?limit=100\")\n }\n\n private func get<T: Decodable>(_ route: String) async throws -> T {\n let request = URLRequest(url: try routeURL(route))\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func post<T: Decodable>(_ route: String, body: [String: Any]) async throws -> T {\n var request = URLRequest(url: try routeURL(route))\n request.httpMethod = \"POST\"\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: body, options: [])\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func routeURL(_ route: String) throws -> URL {\n guard let url = URL(string: route, relativeTo: baseURL)?.absoluteURL else {\n throw AgentAPIError.requestFailed(\"Invalid API route: \\(route)\")\n }\n return url\n }\n\n private func validate(response: URLResponse, data: Data) throws {\n guard let http = response as? HTTPURLResponse, (200..<300).contains(http.statusCode) else {\n let message = String(data: data, encoding: .utf8) ?? \"Request failed.\"\n throw AgentAPIError.requestFailed(message)\n }\n }\n\n private func formattedRequestText(_ text: String) -> String {\n let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)\n guard !trimmed.isEmpty else { return text }\n return \"\"\"\n \\(trimmed)\n\n Response formatting requirements:\n \\(responseFormattingInstruction)\n \"\"\"\n }\n\n private func runtimePayload(settings: AppSettings, secrets: RuntimeSecrets) -> [String: Any] {\n var payload: [String: Any] = [\n \"planner\": settings.planner,\n \"approve_high_risk\": settings.approveHighRisk,\n \"model_provider\": apiModelProvider(settings.modelProvider),\n \"model\": settings.modelName,\n \"model_api_key_env\": modelKeyName(settings.modelProvider)\n ]\n if !settings.modelBaseURL.isEmpty {\n payload[\"model_base_url\"] = settings.modelBaseURL\n }\n let activeProvider = settings.activeModelProvider.trimmingCharacters(in: .whitespacesAndNewlines)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n payload[\"active_model_provider\"] = apiModelProvider(activeProvider)\n payload[\"active_model_api_key_env\"] = modelKeyName(activeProvider)\n if !settings.activeModelName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n payload[\"active_model\"] = settings.activeModelName\n }\n if !settings.activeModelBaseURL.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n payload[\"active_model_base_url\"] = settings.activeModelBaseURL\n }\n }\n\n var secretPayload: [String: String] = [:]\n addSecret(&secretPayload, name: modelKeyName(settings.modelProvider), value: secrets.modelAPIKey)\n if !activeProvider.isEmpty, activeProvider != \"same-as-main\" {\n addSecret(&secretPayload, name: modelKeyName(activeProvider), value: secrets.activeModelAPIKey)\n }\n addSecret(&secretPayload, name: \"DEEPGRAM_API_KEY\", value: secrets.deepgramAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_API_KEY\", value: secrets.elevenLabsAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_VOICE_ID\", value: settings.voiceId)\n addSecret(&secretPayload, name: \"ELEVENLABS_MODEL_ID\", value: settings.elevenLabsModel)\n for channel in settings.channels {\n addSecret(&secretPayload, name: channel.secretName, value: channel.secretValue)\n for item in channel.secretValues {\n addSecret(&secretPayload, name: item.key, value: item.value)\n }\n }\n if !secretPayload.isEmpty {\n payload[\"runtime_secrets\"] = secretPayload\n }\n return payload\n }\n\n private func addSecret(_ secrets: inout [String: String], name: String, value: String) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n let cleanValue = value.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, !cleanValue.isEmpty {\n secrets[cleanName] = cleanValue\n }\n }\n\n private func modelKeyName(_ provider: String) -> String {\n switch provider {\n case \"openrouter\": \"OPENROUTER_API_KEY\"\n case \"nous\": \"NOUS_API_KEY\"\n case \"novita\": \"NOVITA_API_KEY\"\n case \"lmstudio\": \"LM_API_KEY\"\n case \"anthropic\": \"ANTHROPIC_API_KEY\"\n case \"alibaba\": \"DASHSCOPE_API_KEY\"\n case \"groq\": \"GROQ_API_KEY\"\n case \"grok\", \"xai\": \"XAI_API_KEY\"\n case \"gemini\": \"GOOGLE_API_KEY\"\n case \"deepseek\": \"DEEPSEEK_API_KEY\"\n case \"mistral\": \"MISTRAL_API_KEY\"\n case \"cerebras\": \"CEREBRAS_API_KEY\"\n case \"ollama\": \"OLLAMA_API_KEY\"\n case \"ollama-cloud\": \"OLLAMA_API_KEY\"\n case \"local-openai\": \"LOCAL_LLM_API_KEY\"\n case \"vercel\": \"AI_GATEWAY_API_KEY\"\n case \"litellm\": \"LITELLM_API_KEY\"\n case \"nvidia\": \"NVIDIA_API_KEY\"\n case \"huggingface\": \"HF_TOKEN\"\n case \"zai\": \"GLM_API_KEY\"\n case \"kimi-coding\": \"KIMI_API_KEY\"\n case \"kimi-coding-cn\": \"KIMI_CN_API_KEY\"\n case \"stepfun\": \"STEPFUN_API_KEY\"\n case \"minimax\": \"MINIMAX_API_KEY\"\n case \"minimax-cn\": \"MINIMAX_CN_API_KEY\"\n case \"arcee\": \"ARCEEAI_API_KEY\"\n case \"gmi\": \"GMI_API_KEY\"\n case \"xiaomi\": \"XIAOMI_API_KEY\"\n case \"tencent-tokenhub\": \"TOKENHUB_API_KEY\"\n case \"opencode-zen\": \"OPENCODE_ZEN_API_KEY\"\n case \"opencode-go\": \"OPENCODE_GO_API_KEY\"\n case \"kilocode\": \"KILOCODE_API_KEY\"\n case \"azure-openai\": \"AZURE_OPENAI_API_KEY\"\n case \"azure-foundry\": \"AZURE_FOUNDRY_API_KEY\"\n case \"copilot\", \"copilot-acp\": \"GITHUB_TOKEN\"\n case \"bedrock\": \"AWS_ACCESS_KEY_ID\"\n case \"browser-use-cloud\": \"BROWSER_USE_API_KEY\"\n default: \"OPENAI_API_KEY\"\n }\n }\n\n private func apiModelProvider(_ provider: String) -> String {\n provider == \"openai\" ? \"openai-responses\" : provider\n }\n\n private func channelSecretRefs(_ setup: ChannelSetup) -> [String: String] {\n var refs: [String: String] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n refs[\"primary\"] = primary\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n refs[cleanName] = cleanName\n }\n }\n return refs\n }\n\n private func channelSecretConfigured(_ setup: ChannelSetup) -> [String: Bool] {\n var configured: [String: Bool] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n configured[\"primary\"] = setup.secretConfigured || !setup.secretValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n configured[cleanName] = !setup.secretValues[cleanName, default: \"\"].trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n }\n return configured\n }\n\n private func cleanList(_ values: [String]) -> [String] {\n values.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }.filter { !$0.isEmpty }\n }\n\n private func removeEmptyStrings(from payload: inout [String: Any]) {\n for (key, value) in payload {\n if let string = value as? String, string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {\n payload.removeValue(forKey: key)\n }\n if let strings = value as? [String], strings.isEmpty {\n payload.removeValue(forKey: key)\n }\n }\n }\n\n private static func normalizedBaseURL(_ value: String, fallback: URL = URL(string: \"http://127.0.0.1:8765/\")!) -> URL {\n let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)\n .trimmingCharacters(in: CharacterSet(charactersIn: \"/\"))\n guard !trimmed.isEmpty, let url = URL(string: \"\\(trimmed)/\") else {\n return fallback\n }\n return url\n }\n\n private static func urlEncode(_ value: String) -> String {\n value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? value\n }\n}\n\nenum AgentAPIError: LocalizedError {\n case requestFailed(String)\n\n var errorDescription: String? {\n switch self {\n case .requestFailed(let message): message\n }\n }\n}\n\nprivate struct ServerSentEventParser {\n private var eventName = \"message\"\n private var dataLines: [String] = []\n private let decoder = JSONDecoder()\n\n mutating func consume(_ line: String) throws -> AgentStreamEvent? {\n if line.hasPrefix(\"event: \") {\n eventName = String(line.dropFirst(\"event: \".count)).trimmingCharacters(in: .whitespaces)\n return nil\n }\n if line.hasPrefix(\"data: \") {\n dataLines.append(String(line.dropFirst(\"data: \".count)))\n return try finish()\n }\n if line.isEmpty {\n return try finish()\n }\n return nil\n }\n\n mutating func finish() throws -> AgentStreamEvent? {\n guard !dataLines.isEmpty else { return nil }\n let data = Data(dataLines.joined(separator: \"\\n\").utf8)\n let value = try decoder.decode(JSONValue.self, from: data)\n let event = AgentStreamEvent(event: eventName, data: value)\n eventName = \"message\"\n dataLines = []\n return event\n }\n}\n"}, "0f91a0d2387b42a7fd6f41a54687733d878010f6": {"t": 1785756647.3385706, "n": 21701, "h": "a3f7aef3df0ff28b", "c": "import Foundation\n\nstruct RuntimeSecrets {\n var modelAPIKey = \"\"\n var deepgramAPIKey = \"\"\n var elevenLabsAPIKey = \"\"\n}\n\n@MainActor\nfinal class AgentAPIClient {\n private var baseURL: URL\n private let decoder: JSONDecoder\n private let encoder = JSONEncoder()\n private let responseFormattingInstruction = \"\"\"\n Respond with a detailed final answer in GitHub-flavored Markdown. Use clear sections, bullets, and tables where they make the answer easier to scan. Include caveats, source/tool evidence, and next steps when relevant. Do not return a single unformatted paragraph unless the user explicitly asks for a very short answer.\n \"\"\"\n\n init(baseURL: String) {\n self.baseURL = AgentAPIClient.normalizedBaseURL(baseURL)\n decoder = JSONDecoder()\n }\n\n func setBaseURL(_ value: String) {\n baseURL = AgentAPIClient.normalizedBaseURL(value, fallback: baseURL)\n }\n\n func health() async throws -> HealthPayload {\n try await get(\"health\")\n }\n\n func systemStatus() async throws -> JSONValue {\n try await get(\"system/status\")\n }\n\n func latestUpdate() async throws -> UpdateInfo {\n try await get(\"updates/latest?platform=macos\")\n }\n\n func tools() async throws -> ToolCatalog {\n try await get(\"tools\")\n }\n\n func runs() async throws -> [RunItem] {\n try await get(\"runs?limit=30\")\n }\n\n func approvals() async throws -> [ApprovalItem] {\n try await get(\"approvals?status=pending&limit=30\")\n }\n\n func channels() async throws -> [ChannelInfo] {\n try await get(\"channels\")\n }\n\n func outbox() async throws -> OutboxEnvelope {\n try await get(\"channels/outbox\")\n }\n\n func channelStatus(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/status\", body: payload)\n }\n\n func channelDoctor(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/doctor\", body: payload)\n }\n\n func channelRequirements(channelID: String) async throws -> JSONValue {\n try await get(\"channels/requirements?channel_id=\\(Self.urlEncode(channelID))\")\n }\n\n func runChannelSmoke(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"prepare_messages\"] = true\n payload[\"dry_run_sends\"] = true\n return try await post(\"channels/smoke\", body: payload)\n }\n\n func channelListeners(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/listeners\", body: payload)\n }\n\n func tickChannelListener(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func tickAllChannelListeners(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func saveChannelSetup(channel: ChannelInfo, setup: ChannelSetup) async throws -> JSONValue {\n let payload: [String: Any] = [\n \"channel_id\": channel.channelId,\n \"enabled\": setup.enabled,\n \"listen_enabled\": setup.listenEnabled,\n \"conversation_defaults\": [\n \"conversation_id\": setup.conversationId,\n \"conversation_type\": setup.conversationType\n ],\n \"secret_refs\": channelSecretRefs(setup),\n \"secret_configured\": channelSecretConfigured(setup),\n \"allowlist\": cleanList(setup.allowlist),\n \"group_allowlist\": cleanList(setup.groupAllowlist),\n \"notes\": setup.notes\n ]\n return try await post(\"channels/setup\", body: payload)\n }\n\n func prepareChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Prepared from the macOS app channel panel.\"\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/prepare\", body: payload)\n }\n\n func sendChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Approval-gated send from the macOS app channel panel.\"\n payload[\"approve_high_risk\"] = settings.approveHighRisk\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/send\", body: payload)\n }\n\n func sendChannelInbound(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"conversation_type\"] = setup.conversationType.isEmpty ? \"dm\" : setup.conversationType\n payload[\"sender_id\"] = \"macos-app\"\n payload[\"text\"] = text\n payload[\"requires_response\"] = true\n payload[\"prepare_reply\"] = true\n return try await post(\"channels/inbound\", body: payload)\n }\n\n func voiceStatus(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n try await post(\"voice/status\", body: runtimePayload(settings: settings, secrets: secrets))\n }\n\n func transcribeAudio(_ audioURL: URL, settings: AppSettings, secrets: RuntimeSecrets) async throws -> String {\n let audioData = try Data(contentsOf: audioURL)\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"audio_base64\"] = audioData.base64EncodedString()\n payload[\"filename\"] = audioURL.lastPathComponent\n payload[\"provider\"] = secrets.deepgramAPIKey.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? \"local-whisper\" : \"deepgram\"\n payload[\"smart_format\"] = true\n payload[\"mime_type\"] = \"audio/mp4\"\n payload[\"reason\"] = \"macOS wake listener captured the user's post-wake task.\"\n let result: JSONValue = try await post(\"voice/transcribe\", body: payload)\n let transcript = result[\"transcript\"]?.stringValue?.trimmingCharacters(in: .whitespacesAndNewlines) ?? \"\"\n guard !transcript.isEmpty else {\n let summary = result[\"summary\"]?.stringValue ?? \"No transcript was returned.\"\n let providerErrors = result[\"provider_errors\"]?.arrayValue?.compactMap { item in\n item[\"error\"]?.stringValue\n }.joined(separator: \" \")\n let detail = providerErrors?.isEmpty == false ? \"\\(summary) \\(providerErrors ?? \"\")\" : summary\n throw AgentAPIError.requestFailed(detail)\n }\n return transcript\n }\n\n func autonomousStatus() async throws -> JSONValue {\n try await get(\"autonomous/status?limit=10\")\n }\n\n func runAutonomousCycle(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"max_cycles\"] = settings.maxCycles\n payload[\"idle_sleep_seconds\"] = 0\n payload[\"stop_after_idle_cycles\"] = 1\n payload[\"allow_initiative\"] = settings.allowInitiative\n return try await post(\"autonomous/cycles\", body: payload)\n }\n\n func sendStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> AgentRunResponse {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"text\"] = formattedRequestText(text)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": \"detailed\"\n ]\n return try await post(\"stimuli\", body: payload)\n }\n\n func streamStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) throws -> AsyncThrowingStream<AgentStreamEvent, Error> {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"text\"] = formattedRequestText(text)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": \"detailed\"\n ]\n\n var request = URLRequest(url: try routeURL(\"stimuli/stream\"))\n request.httpMethod = \"POST\"\n request.timeoutInterval = 300\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: payload, options: [])\n\n return AsyncThrowingStream { continuation in\n let streamTask = Task {\n do {\n let (bytes, response) = try await URLSession.shared.bytes(for: request)\n if let http = response as? HTTPURLResponse, !(200..<300).contains(http.statusCode) {\n throw AgentAPIError.requestFailed(\"stimuli/stream failed with HTTP \\(http.statusCode).\")\n }\n var parser = ServerSentEventParser()\n for try await line in bytes.lines {\n if Task.isCancelled { break }\n if let event = try parser.consume(line) {\n continuation.yield(event)\n }\n }\n if Task.isCancelled {\n continuation.finish(throwing: CancellationError())\n return\n }\n if let event = try parser.finish() {\n continuation.yield(event)\n }\n continuation.finish()\n } catch {\n continuation.finish(throwing: error)\n }\n }\n continuation.onTermination = { _ in\n streamTask.cancel()\n }\n }\n }\n\n func approve(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/approve\", body: [\"note\": note])\n }\n\n func reject(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/reject\", body: [\"note\": note])\n }\n\n func cancelRun(_ runID: String, reason: String) async throws -> JSONValue {\n try await post(\"runs/\\(runID)/cancel\", body: [\"reason\": reason])\n }\n\n func runTimeline(_ runID: String) async throws -> JSONValue {\n try await get(\"runs/\\(runID)/timeline?limit=100\")\n }\n\n private func get<T: Decodable>(_ route: String) async throws -> T {\n let request = URLRequest(url: try routeURL(route))\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func post<T: Decodable>(_ route: String, body: [String: Any]) async throws -> T {\n var request = URLRequest(url: try routeURL(route))\n request.httpMethod = \"POST\"\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: body, options: [])\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func routeURL(_ route: String) throws -> URL {\n guard let url = URL(string: route, relativeTo: baseURL)?.absoluteURL else {\n throw AgentAPIError.requestFailed(\"Invalid API route: \\(route)\")\n }\n return url\n }\n\n private func validate(response: URLResponse, data: Data) throws {\n guard let http = response as? HTTPURLResponse, (200..<300).contains(http.statusCode) else {\n let message = String(data: data, encoding: .utf8) ?? \"Request failed.\"\n throw AgentAPIError.requestFailed(message)\n }\n }\n\n private func formattedRequestText(_ text: String) -> String {\n let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)\n guard !trimmed.isEmpty else { return text }\n return \"\"\"\n \\(trimmed)\n\n Response formatting requirements:\n \\(responseFormattingInstruction)\n \"\"\"\n }\n\n private func runtimePayload(settings: AppSettings, secrets: RuntimeSecrets) -> [String: Any] {\n var payload: [String: Any] = [\n \"planner\": settings.planner,\n \"approve_high_risk\": settings.approveHighRisk,\n \"model_provider\": apiModelProvider(settings.modelProvider),\n \"model\": settings.modelName,\n \"model_api_key_env\": modelKeyName(settings.modelProvider)\n ]\n if !settings.modelBaseURL.isEmpty {\n payload[\"model_base_url\"] = settings.modelBaseURL\n }\n\n var secretPayload: [String: String] = [:]\n addSecret(&secretPayload, name: modelKeyName(settings.modelProvider), value: secrets.modelAPIKey)\n addSecret(&secretPayload, name: \"DEEPGRAM_API_KEY\", value: secrets.deepgramAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_API_KEY\", value: secrets.elevenLabsAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_VOICE_ID\", value: settings.voiceId)\n addSecret(&secretPayload, name: \"ELEVENLABS_MODEL_ID\", value: settings.elevenLabsModel)\n for channel in settings.channels {\n addSecret(&secretPayload, name: channel.secretName, value: channel.secretValue)\n for item in channel.secretValues {\n addSecret(&secretPayload, name: item.key, value: item.value)\n }\n }\n if !secretPayload.isEmpty {\n payload[\"runtime_secrets\"] = secretPayload\n }\n return payload\n }\n\n private func addSecret(_ secrets: inout [String: String], name: String, value: String) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n let cleanValue = value.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, !cleanValue.isEmpty {\n secrets[cleanName] = cleanValue\n }\n }\n\n private func modelKeyName(_ provider: String) -> String {\n switch provider {\n case \"openrouter\": \"OPENROUTER_API_KEY\"\n case \"nous\": \"NOUS_API_KEY\"\n case \"novita\": \"NOVITA_API_KEY\"\n case \"lmstudio\": \"LM_API_KEY\"\n case \"anthropic\": \"ANTHROPIC_API_KEY\"\n case \"alibaba\": \"DASHSCOPE_API_KEY\"\n case \"groq\": \"GROQ_API_KEY\"\n case \"grok\", \"xai\": \"XAI_API_KEY\"\n case \"gemini\": \"GOOGLE_API_KEY\"\n case \"deepseek\": \"DEEPSEEK_API_KEY\"\n case \"mistral\": \"MISTRAL_API_KEY\"\n case \"cerebras\": \"CEREBRAS_API_KEY\"\n case \"ollama\": \"OLLAMA_API_KEY\"\n case \"ollama-cloud\": \"OLLAMA_API_KEY\"\n case \"local-openai\": \"LOCAL_LLM_API_KEY\"\n case \"vercel\": \"AI_GATEWAY_API_KEY\"\n case \"litellm\": \"LITELLM_API_KEY\"\n case \"nvidia\": \"NVIDIA_API_KEY\"\n case \"huggingface\": \"HF_TOKEN\"\n case \"zai\": \"GLM_API_KEY\"\n case \"kimi-coding\": \"KIMI_API_KEY\"\n case \"kimi-coding-cn\": \"KIMI_CN_API_KEY\"\n case \"stepfun\": \"STEPFUN_API_KEY\"\n case \"minimax\": \"MINIMAX_API_KEY\"\n case \"minimax-cn\": \"MINIMAX_CN_API_KEY\"\n case \"arcee\": \"ARCEEAI_API_KEY\"\n case \"gmi\": \"GMI_API_KEY\"\n case \"xiaomi\": \"XIAOMI_API_KEY\"\n case \"tencent-tokenhub\": \"TOKENHUB_API_KEY\"\n case \"opencode-zen\": \"OPENCODE_ZEN_API_KEY\"\n case \"opencode-go\": \"OPENCODE_GO_API_KEY\"\n case \"kilocode\": \"KILOCODE_API_KEY\"\n case \"azure-openai\": \"AZURE_OPENAI_API_KEY\"\n case \"azure-foundry\": \"AZURE_FOUNDRY_API_KEY\"\n case \"copilot\", \"copilot-acp\": \"GITHUB_TOKEN\"\n case \"bedrock\": \"AWS_ACCESS_KEY_ID\"\n case \"browser-use-cloud\": \"BROWSER_USE_API_KEY\"\n default: \"OPENAI_API_KEY\"\n }\n }\n\n private func apiModelProvider(_ provider: String) -> String {\n provider == \"openai\" ? \"openai-responses\" : provider\n }\n\n private func channelSecretRefs(_ setup: ChannelSetup) -> [String: String] {\n var refs: [String: String] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n refs[\"primary\"] = primary\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n refs[cleanName] = cleanName\n }\n }\n return refs\n }\n\n private func channelSecretConfigured(_ setup: ChannelSetup) -> [String: Bool] {\n var configured: [String: Bool] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n configured[\"primary\"] = setup.secretConfigured || !setup.secretValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n configured[cleanName] = !setup.secretValues[cleanName, default: \"\"].trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n }\n return configured\n }\n\n private func cleanList(_ values: [String]) -> [String] {\n values.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }.filter { !$0.isEmpty }\n }\n\n private static func normalizedBaseURL(_ value: String, fallback: URL = URL(string: \"http://127.0.0.1:8765/\")!) -> URL {\n let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)\n .trimmingCharacters(in: CharacterSet(charactersIn: \"/\"))\n guard !trimmed.isEmpty, let url = URL(string: \"\\(trimmed)/\") else {\n return fallback\n }\n return url\n }\n\n private static func urlEncode(_ value: String) -> String {\n value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? value\n }\n}\n\nenum AgentAPIError: LocalizedError {\n case requestFailed(String)\n\n var errorDescription: String? {\n switch self {\n case .requestFailed(let message): message\n }\n }\n}\n\nprivate struct ServerSentEventParser {\n private var eventName = \"message\"\n private var dataLines: [String] = []\n private let decoder = JSONDecoder()\n\n mutating func consume(_ line: String) throws -> AgentStreamEvent? {\n if line.hasPrefix(\"event: \") {\n eventName = String(line.dropFirst(\"event: \".count)).trimmingCharacters(in: .whitespaces)\n return nil\n }\n if line.hasPrefix(\"data: \") {\n dataLines.append(String(line.dropFirst(\"data: \".count)))\n return try finish()\n }\n if line.isEmpty {\n return try finish()\n }\n return nil\n }\n\n mutating func finish() throws -> AgentStreamEvent? {\n guard !dataLines.isEmpty else { return nil }\n let data = Data(dataLines.joined(separator: \"\\n\").utf8)\n let value = try decoder.decode(JSONValue.self, from: data)\n let event = AgentStreamEvent(event: eventName, data: value)\n eventName = \"message\"\n dataLines = []\n return event\n }\n}\n"}, "bf989d47825abc5ce2e6ff1c2bd80e5ac1f801c5": {"t": 1785756648.6608706, "n": 20231, "h": "82c37012ba802922", "c": "import Foundation\n\nstruct RuntimeSecrets {\n var modelAPIKey = \"\"\n var deepgramAPIKey = \"\"\n var elevenLabsAPIKey = \"\"\n}\n\n@MainActor\nfinal class AgentAPIClient {\n private var baseURL: URL\n private let decoder: JSONDecoder\n private let encoder = JSONEncoder()\n private let responseFormattingInstruction = \"\"\"\n Respond with a detailed final answer in GitHub-flavored Markdown. Use clear sections, bullets, and tables where they make the answer easier to scan. Include caveats, source/tool evidence, and next steps when relevant. Do not return a single unformatted paragraph unless the user explicitly asks for a very short answer.\n \"\"\"\n\n init(baseURL: String) {\n self.baseURL = AgentAPIClient.normalizedBaseURL(baseURL)\n decoder = JSONDecoder()\n }\n\n func setBaseURL(_ value: String) {\n baseURL = AgentAPIClient.normalizedBaseURL(value, fallback: baseURL)\n }\n\n func health() async throws -> HealthPayload {\n try await get(\"health\")\n }\n\n func systemStatus() async throws -> JSONValue {\n try await get(\"system/status\")\n }\n\n func latestUpdate() async throws -> UpdateInfo {\n try await get(\"updates/latest?platform=macos\")\n }\n\n func tools() async throws -> ToolCatalog {\n try await get(\"tools\")\n }\n\n func runs() async throws -> [RunItem] {\n try await get(\"runs?limit=30\")\n }\n\n func approvals() async throws -> [ApprovalItem] {\n try await get(\"approvals?status=pending&limit=30\")\n }\n\n func channels() async throws -> [ChannelInfo] {\n try await get(\"channels\")\n }\n\n func outbox() async throws -> OutboxEnvelope {\n try await get(\"channels/outbox\")\n }\n\n func channelStatus(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/status\", body: payload)\n }\n\n func channelDoctor(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/doctor\", body: payload)\n }\n\n func channelRequirements(channelID: String) async throws -> JSONValue {\n try await get(\"channels/requirements?channel_id=\\(Self.urlEncode(channelID))\")\n }\n\n func runChannelSmoke(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"prepare_messages\"] = true\n payload[\"dry_run_sends\"] = true\n return try await post(\"channels/smoke\", body: payload)\n }\n\n func channelListeners(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/listeners\", body: payload)\n }\n\n func tickChannelListener(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func tickAllChannelListeners(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func saveChannelSetup(channel: ChannelInfo, setup: ChannelSetup) async throws -> JSONValue {\n let payload: [String: Any] = [\n \"channel_id\": channel.channelId,\n \"enabled\": setup.enabled,\n \"listen_enabled\": setup.listenEnabled,\n \"conversation_defaults\": [\n \"conversation_id\": setup.conversationId,\n \"conversation_type\": setup.conversationType\n ],\n \"secret_refs\": channelSecretRefs(setup),\n \"secret_configured\": channelSecretConfigured(setup),\n \"allowlist\": cleanList(setup.allowlist),\n \"group_allowlist\": cleanList(setup.groupAllowlist),\n \"notes\": setup.notes\n ]\n return try await post(\"channels/setup\", body: payload)\n }\n\n func prepareChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Prepared from the macOS app channel panel.\"\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/prepare\", body: payload)\n }\n\n func sendChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Approval-gated send from the macOS app channel panel.\"\n payload[\"approve_high_risk\"] = settings.approveHighRisk\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/send\", body: payload)\n }\n\n func sendChannelInbound(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"conversation_type\"] = setup.conversationType.isEmpty ? \"dm\" : setup.conversationType\n payload[\"sender_id\"] = \"macos-app\"\n payload[\"text\"] = text\n payload[\"requires_response\"] = true\n payload[\"prepare_reply\"] = true\n return try await post(\"channels/inbound\", body: payload)\n }\n\n func voiceStatus(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n try await post(\"voice/status\", body: runtimePayload(settings: settings, secrets: secrets))\n }\n\n func transcribeAudio(_ audioURL: URL, settings: AppSettings, secrets: RuntimeSecrets) async throws -> String {\n let audioData = try Data(contentsOf: audioURL)\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"audio_base64\"] = audioData.base64EncodedString()\n payload[\"filename\"] = audioURL.lastPathComponent\n payload[\"provider\"] = secrets.deepgramAPIKey.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? \"local-whisper\" : \"deepgram\"\n payload[\"smart_format\"] = true\n payload[\"mime_type\"] = \"audio/mp4\"\n payload[\"reason\"] = \"macOS wake listener captured the user's post-wake task.\"\n let result: JSONValue = try await post(\"voice/transcribe\", body: payload)\n let transcript = result[\"transcript\"]?.stringValue?.trimmingCharacters(in: .whitespacesAndNewlines) ?? \"\"\n guard !transcript.isEmpty else {\n let summary = result[\"summary\"]?.stringValue ?? \"No transcript was returned.\"\n let providerErrors = result[\"provider_errors\"]?.arrayValue?.compactMap { item in\n item[\"error\"]?.stringValue\n }.joined(separator: \" \")\n let detail = providerErrors?.isEmpty == false ? \"\\(summary) \\(providerErrors ?? \"\")\" : summary\n throw AgentAPIError.requestFailed(detail)\n }\n return transcript\n }\n\n func autonomousStatus() async throws -> JSONValue {\n try await get(\"autonomous/status?limit=10\")\n }\n\n func runAutonomousCycle(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"max_cycles\"] = settings.maxCycles\n payload[\"idle_sleep_seconds\"] = 0\n payload[\"stop_after_idle_cycles\"] = 1\n payload[\"allow_initiative\"] = settings.allowInitiative\n return try await post(\"autonomous/cycles\", body: payload)\n }\n\n func sendStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> AgentRunResponse {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"text\"] = formattedRequestText(text)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": \"detailed\"\n ]\n return try await post(\"stimuli\", body: payload)\n }\n\n func streamStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) throws -> AsyncThrowingStream<AgentStreamEvent, Error> {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"text\"] = formattedRequestText(text)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": \"detailed\"\n ]\n\n var request = URLRequest(url: try routeURL(\"stimuli/stream\"))\n request.httpMethod = \"POST\"\n request.timeoutInterval = 300\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: payload, options: [])\n\n return AsyncThrowingStream { continuation in\n let streamTask = Task {\n do {\n let (bytes, response) = try await URLSession.shared.bytes(for: request)\n if let http = response as? HTTPURLResponse, !(200..<300).contains(http.statusCode) {\n throw AgentAPIError.requestFailed(\"stimuli/stream failed with HTTP \\(http.statusCode).\")\n }\n var parser = ServerSentEventParser()\n for try await line in bytes.lines {\n if Task.isCancelled { break }\n if let event = try parser.consume(line) {\n continuation.yield(event)\n }\n }\n if Task.isCancelled {\n continuation.finish(throwing: CancellationError())\n return\n }\n if let event = try parser.finish() {\n continuation.yield(event)\n }\n continuation.finish()\n } catch {\n continuation.finish(throwing: error)\n }\n }\n continuation.onTermination = { _ in\n streamTask.cancel()\n }\n }\n }\n\n func approve(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/approve\", body: [\"note\": note])\n }\n\n func reject(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/reject\", body: [\"note\": note])\n }\n\n func cancelRun(_ runID: String, reason: String) async throws -> JSONValue {\n try await post(\"runs/\\(runID)/cancel\", body: [\"reason\": reason])\n }\n\n func runTimeline(_ runID: String) async throws -> JSONValue {\n try await get(\"runs/\\(runID)/timeline?limit=100\")\n }\n\n private func get<T: Decodable>(_ route: String) async throws -> T {\n let request = URLRequest(url: try routeURL(route))\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func post<T: Decodable>(_ route: String, body: [String: Any]) async throws -> T {\n var request = URLRequest(url: try routeURL(route))\n request.httpMethod = \"POST\"\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: body, options: [])\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func routeURL(_ route: String) throws -> URL {\n guard let url = URL(string: route, relativeTo: baseURL)?.absoluteURL else {\n throw AgentAPIError.requestFailed(\"Invalid API route: \\(route)\")\n }\n return url\n }\n\n private func validate(response: URLResponse, data: Data) throws {\n guard let http = response as? HTTPURLResponse, (200..<300).contains(http.statusCode) else {\n let message = String(data: data, encoding: .utf8) ?? \"Request failed.\"\n throw AgentAPIError.requestFailed(message)\n }\n }\n\n private func formattedRequestText(_ text: String) -> String {\n let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)\n guard !trimmed.isEmpty else { return text }\n return \"\"\"\n \\(trimmed)\n\n Response formatting requirements:\n \\(responseFormattingInstruction)\n \"\"\"\n }\n\n private func runtimePayload(settings: AppSettings, secrets: RuntimeSecrets) -> [String: Any] {\n var payload: [String: Any] = [\n \"planner\": settings.planner,\n \"approve_high_risk\": settings.approveHighRisk,\n \"model_provider\": apiModelProvider(settings.modelProvider),\n \"model\": settings.modelName,\n \"model_api_key_env\": modelKeyName(settings.modelProvider)\n ]\n if !settings.modelBaseURL.isEmpty {\n payload[\"model_base_url\"] = settings.modelBaseURL\n }\n\n var secretPayload: [String: String] = [:]\n addSecret(&secretPayload, name: modelKeyName(settings.modelProvider), value: secrets.modelAPIKey)\n addSecret(&secretPayload, name: \"DEEPGRAM_API_KEY\", value: secrets.deepgramAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_API_KEY\", value: secrets.elevenLabsAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_VOICE_ID\", value: settings.voiceId)\n addSecret(&secretPayload, name: \"ELEVENLABS_MODEL_ID\", value: settings.elevenLabsModel)\n for channel in settings.channels {\n addSecret(&secretPayload, name: channel.secretName, value: channel.secretValue)\n for item in channel.secretValues {\n addSecret(&secretPayload, name: item.key, value: item.value)\n }\n }\n if !secretPayload.isEmpty {\n payload[\"runtime_secrets\"] = secretPayload\n }\n return payload\n }\n\n private func addSecret(_ secrets: inout [String: String], name: String, value: String) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n let cleanValue = value.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, !cleanValue.isEmpty {\n secrets[cleanName] = cleanValue\n }\n }\n\n private func modelKeyName(_ provider: String) -> String {\n switch provider {\n case \"groq\": \"GROQ_API_KEY\"\n case \"grok\": \"XAI_API_KEY\"\n case \"ollama\": \"OLLAMA_API_KEY\"\n case \"local-openai\": \"LOCAL_LLM_API_KEY\"\n default: \"OPENAI_API_KEY\"\n }\n }\n\n private func apiModelProvider(_ provider: String) -> String {\n provider == \"openai\" ? \"openai-responses\" : provider\n }\n\n private func channelSecretRefs(_ setup: ChannelSetup) -> [String: String] {\n var refs: [String: String] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n refs[\"primary\"] = primary\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n refs[cleanName] = cleanName\n }\n }\n return refs\n }\n\n private func channelSecretConfigured(_ setup: ChannelSetup) -> [String: Bool] {\n var configured: [String: Bool] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n configured[\"primary\"] = setup.secretConfigured || !setup.secretValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n configured[cleanName] = !setup.secretValues[cleanName, default: \"\"].trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n }\n return configured\n }\n\n private func cleanList(_ values: [String]) -> [String] {\n values.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }.filter { !$0.isEmpty }\n }\n\n private static func normalizedBaseURL(_ value: String, fallback: URL = URL(string: \"http://127.0.0.1:8765/\")!) -> URL {\n let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)\n .trimmingCharacters(in: CharacterSet(charactersIn: \"/\"))\n guard !trimmed.isEmpty, let url = URL(string: \"\\(trimmed)/\") else {\n return fallback\n }\n return url\n }\n\n private static func urlEncode(_ value: String) -> String {\n value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? value\n }\n}\n\nenum AgentAPIError: LocalizedError {\n case requestFailed(String)\n\n var errorDescription: String? {\n switch self {\n case .requestFailed(let message): message\n }\n }\n}\n\nprivate struct ServerSentEventParser {\n private var eventName = \"message\"\n private var dataLines: [String] = []\n private let decoder = JSONDecoder()\n\n mutating func consume(_ line: String) throws -> AgentStreamEvent? {\n if line.hasPrefix(\"event: \") {\n eventName = String(line.dropFirst(\"event: \".count)).trimmingCharacters(in: .whitespaces)\n return nil\n }\n if line.hasPrefix(\"data: \") {\n dataLines.append(String(line.dropFirst(\"data: \".count)))\n return try finish()\n }\n if line.isEmpty {\n return try finish()\n }\n return nil\n }\n\n mutating func finish() throws -> AgentStreamEvent? {\n guard !dataLines.isEmpty else { return nil }\n let data = Data(dataLines.joined(separator: \"\\n\").utf8)\n let value = try decoder.decode(JSONValue.self, from: data)\n let event = AgentStreamEvent(event: eventName, data: value)\n eventName = \"message\"\n dataLines = []\n return event\n }\n}\n"}, "5c2dced2074c44c9d27f9e02763618f55f01ec96": {"t": 1785756650.0553198, "n": 18512, "h": "197432067aedfbea", "c": "import Foundation\n\nstruct RuntimeSecrets {\n var modelAPIKey = \"\"\n var deepgramAPIKey = \"\"\n var elevenLabsAPIKey = \"\"\n}\n\n@MainActor\nfinal class AgentAPIClient {\n private var baseURL: URL\n private let decoder: JSONDecoder\n private let encoder = JSONEncoder()\n private let responseFormattingInstruction = \"\"\"\n Respond with a detailed final answer in GitHub-flavored Markdown. Use clear sections, bullets, and tables where they make the answer easier to scan. Include caveats, source/tool evidence, and next steps when relevant. Do not return a single unformatted paragraph unless the user explicitly asks for a very short answer.\n \"\"\"\n\n init(baseURL: String) {\n self.baseURL = AgentAPIClient.normalizedBaseURL(baseURL)\n decoder = JSONDecoder()\n }\n\n func setBaseURL(_ value: String) {\n baseURL = AgentAPIClient.normalizedBaseURL(value, fallback: baseURL)\n }\n\n func health() async throws -> HealthPayload {\n try await get(\"health\")\n }\n\n func systemStatus() async throws -> JSONValue {\n try await get(\"system/status\")\n }\n\n func latestUpdate() async throws -> UpdateInfo {\n try await get(\"updates/latest?platform=macos\")\n }\n\n func tools() async throws -> ToolCatalog {\n try await get(\"tools\")\n }\n\n func runs() async throws -> [RunItem] {\n try await get(\"runs?limit=30\")\n }\n\n func approvals() async throws -> [ApprovalItem] {\n try await get(\"approvals?status=pending&limit=30\")\n }\n\n func channels() async throws -> [ChannelInfo] {\n try await get(\"channels\")\n }\n\n func outbox() async throws -> OutboxEnvelope {\n try await get(\"channels/outbox\")\n }\n\n func channelStatus(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/status\", body: payload)\n }\n\n func channelDoctor(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/doctor\", body: payload)\n }\n\n func channelRequirements(channelID: String) async throws -> JSONValue {\n try await get(\"channels/requirements?channel_id=\\(Self.urlEncode(channelID))\")\n }\n\n func runChannelSmoke(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"prepare_messages\"] = true\n payload[\"dry_run_sends\"] = true\n return try await post(\"channels/smoke\", body: payload)\n }\n\n func channelListeners(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/listeners\", body: payload)\n }\n\n func tickChannelListener(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func tickAllChannelListeners(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func saveChannelSetup(channel: ChannelInfo, setup: ChannelSetup) async throws -> JSONValue {\n let payload: [String: Any] = [\n \"channel_id\": channel.channelId,\n \"enabled\": setup.enabled,\n \"listen_enabled\": setup.listenEnabled,\n \"conversation_defaults\": [\n \"conversation_id\": setup.conversationId,\n \"conversation_type\": setup.conversationType\n ],\n \"secret_refs\": channelSecretRefs(setup),\n \"secret_configured\": channelSecretConfigured(setup),\n \"allowlist\": cleanList(setup.allowlist),\n \"group_allowlist\": cleanList(setup.groupAllowlist),\n \"notes\": setup.notes\n ]\n return try await post(\"channels/setup\", body: payload)\n }\n\n func prepareChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Prepared from the macOS app channel panel.\"\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/prepare\", body: payload)\n }\n\n func sendChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Approval-gated send from the macOS app channel panel.\"\n payload[\"approve_high_risk\"] = settings.approveHighRisk\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/send\", body: payload)\n }\n\n func sendChannelInbound(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"conversation_type\"] = setup.conversationType.isEmpty ? \"dm\" : setup.conversationType\n payload[\"sender_id\"] = \"macos-app\"\n payload[\"text\"] = text\n payload[\"requires_response\"] = true\n payload[\"prepare_reply\"] = true\n return try await post(\"channels/inbound\", body: payload)\n }\n\n func voiceStatus(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n try await post(\"voice/status\", body: runtimePayload(settings: settings, secrets: secrets))\n }\n\n func autonomousStatus() async throws -> JSONValue {\n try await get(\"autonomous/status?limit=10\")\n }\n\n func runAutonomousCycle(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"max_cycles\"] = settings.maxCycles\n payload[\"idle_sleep_seconds\"] = 0\n payload[\"stop_after_idle_cycles\"] = 1\n payload[\"allow_initiative\"] = settings.allowInitiative\n return try await post(\"autonomous/cycles\", body: payload)\n }\n\n func sendStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> AgentRunResponse {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"text\"] = formattedRequestText(text)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": \"detailed\"\n ]\n return try await post(\"stimuli\", body: payload)\n }\n\n func streamStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) throws -> AsyncThrowingStream<AgentStreamEvent, Error> {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"text\"] = formattedRequestText(text)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": \"detailed\"\n ]\n\n var request = URLRequest(url: try routeURL(\"stimuli/stream\"))\n request.httpMethod = \"POST\"\n request.timeoutInterval = 300\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: payload, options: [])\n\n return AsyncThrowingStream { continuation in\n Task {\n do {\n let (bytes, response) = try await URLSession.shared.bytes(for: request)\n if let http = response as? HTTPURLResponse, !(200..<300).contains(http.statusCode) {\n throw AgentAPIError.requestFailed(\"stimuli/stream failed with HTTP \\(http.statusCode).\")\n }\n var parser = ServerSentEventParser()\n for try await line in bytes.lines {\n if let event = try parser.consume(line) {\n continuation.yield(event)\n }\n }\n if let event = try parser.finish() {\n continuation.yield(event)\n }\n continuation.finish()\n } catch {\n continuation.finish(throwing: error)\n }\n }\n }\n }\n\n func approve(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/approve\", body: [\"note\": note])\n }\n\n func reject(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/reject\", body: [\"note\": note])\n }\n\n func cancelRun(_ runID: String, reason: String) async throws -> JSONValue {\n try await post(\"runs/\\(runID)/cancel\", body: [\"reason\": reason])\n }\n\n func runTimeline(_ runID: String) async throws -> JSONValue {\n try await get(\"runs/\\(runID)/timeline?limit=100\")\n }\n\n private func get<T: Decodable>(_ route: String) async throws -> T {\n let request = URLRequest(url: try routeURL(route))\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func post<T: Decodable>(_ route: String, body: [String: Any]) async throws -> T {\n var request = URLRequest(url: try routeURL(route))\n request.httpMethod = \"POST\"\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: body, options: [])\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func routeURL(_ route: String) throws -> URL {\n guard let url = URL(string: route, relativeTo: baseURL)?.absoluteURL else {\n throw AgentAPIError.requestFailed(\"Invalid API route: \\(route)\")\n }\n return url\n }\n\n private func validate(response: URLResponse, data: Data) throws {\n guard let http = response as? HTTPURLResponse, (200..<300).contains(http.statusCode) else {\n let message = String(data: data, encoding: .utf8) ?? \"Request failed.\"\n throw AgentAPIError.requestFailed(message)\n }\n }\n\n private func formattedRequestText(_ text: String) -> String {\n let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)\n guard !trimmed.isEmpty else { return text }\n return \"\"\"\n \\(trimmed)\n\n Response formatting requirements:\n \\(responseFormattingInstruction)\n \"\"\"\n }\n\n private func runtimePayload(settings: AppSettings, secrets: RuntimeSecrets) -> [String: Any] {\n var payload: [String: Any] = [\n \"planner\": settings.planner,\n \"approve_high_risk\": settings.approveHighRisk,\n \"model_provider\": apiModelProvider(settings.modelProvider),\n \"model\": settings.modelName,\n \"model_api_key_env\": modelKeyName(settings.modelProvider)\n ]\n if !settings.modelBaseURL.isEmpty {\n payload[\"model_base_url\"] = settings.modelBaseURL\n }\n\n var secretPayload: [String: String] = [:]\n addSecret(&secretPayload, name: modelKeyName(settings.modelProvider), value: secrets.modelAPIKey)\n addSecret(&secretPayload, name: \"DEEPGRAM_API_KEY\", value: secrets.deepgramAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_API_KEY\", value: secrets.elevenLabsAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_VOICE_ID\", value: settings.voiceId)\n addSecret(&secretPayload, name: \"ELEVENLABS_MODEL_ID\", value: settings.elevenLabsModel)\n for channel in settings.channels {\n addSecret(&secretPayload, name: channel.secretName, value: channel.secretValue)\n for item in channel.secretValues {\n addSecret(&secretPayload, name: item.key, value: item.value)\n }\n }\n if !secretPayload.isEmpty {\n payload[\"runtime_secrets\"] = secretPayload\n }\n return payload\n }\n\n private func addSecret(_ secrets: inout [String: String], name: String, value: String) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n let cleanValue = value.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, !cleanValue.isEmpty {\n secrets[cleanName] = cleanValue\n }\n }\n\n private func modelKeyName(_ provider: String) -> String {\n switch provider {\n case \"groq\": \"GROQ_API_KEY\"\n case \"grok\": \"XAI_API_KEY\"\n case \"ollama\": \"OLLAMA_API_KEY\"\n case \"local-openai\": \"LOCAL_LLM_API_KEY\"\n default: \"OPENAI_API_KEY\"\n }\n }\n\n private func apiModelProvider(_ provider: String) -> String {\n provider == \"openai\" ? \"openai-responses\" : provider\n }\n\n private func channelSecretRefs(_ setup: ChannelSetup) -> [String: String] {\n var refs: [String: String] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n refs[\"primary\"] = primary\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n refs[cleanName] = cleanName\n }\n }\n return refs\n }\n\n private func channelSecretConfigured(_ setup: ChannelSetup) -> [String: Bool] {\n var configured: [String: Bool] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n configured[\"primary\"] = setup.secretConfigured || !setup.secretValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n configured[cleanName] = !setup.secretValues[cleanName, default: \"\"].trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n }\n return configured\n }\n\n private func cleanList(_ values: [String]) -> [String] {\n values.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }.filter { !$0.isEmpty }\n }\n\n private static func normalizedBaseURL(_ value: String, fallback: URL = URL(string: \"http://127.0.0.1:8765/\")!) -> URL {\n let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)\n .trimmingCharacters(in: CharacterSet(charactersIn: \"/\"))\n guard !trimmed.isEmpty, let url = URL(string: \"\\(trimmed)/\") else {\n return fallback\n }\n return url\n }\n\n private static func urlEncode(_ value: String) -> String {\n value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? value\n }\n}\n\nenum AgentAPIError: LocalizedError {\n case requestFailed(String)\n\n var errorDescription: String? {\n switch self {\n case .requestFailed(let message): message\n }\n }\n}\n\nprivate struct ServerSentEventParser {\n private var eventName = \"message\"\n private var dataLines: [String] = []\n private let decoder = JSONDecoder()\n\n mutating func consume(_ line: String) throws -> AgentStreamEvent? {\n if line.hasPrefix(\"event: \") {\n eventName = String(line.dropFirst(\"event: \".count)).trimmingCharacters(in: .whitespaces)\n return nil\n }\n if line.hasPrefix(\"data: \") {\n dataLines.append(String(line.dropFirst(\"data: \".count)))\n return try finish()\n }\n if line.isEmpty {\n return try finish()\n }\n return nil\n }\n\n mutating func finish() throws -> AgentStreamEvent? {\n guard !dataLines.isEmpty else { return nil }\n let data = Data(dataLines.joined(separator: \"\\n\").utf8)\n let value = try decoder.decode(JSONValue.self, from: data)\n let event = AgentStreamEvent(event: eventName, data: value)\n eventName = \"message\"\n dataLines = []\n return event\n }\n}\n"}, "fb542e97770c71aaa0e8b9ca6be1e8692aa3f873": {"t": 1785756651.2596054, "n": 18397, "h": "bbf85917cc612382", "c": "import Foundation\n\nstruct RuntimeSecrets {\n var modelAPIKey = \"\"\n var deepgramAPIKey = \"\"\n var elevenLabsAPIKey = \"\"\n}\n\n@MainActor\nfinal class AgentAPIClient {\n private var baseURL: URL\n private let decoder: JSONDecoder\n private let encoder = JSONEncoder()\n private let responseFormattingInstruction = \"\"\"\n Respond with a detailed final answer in GitHub-flavored Markdown. Use clear sections, bullets, and tables where they make the answer easier to scan. Include caveats, source/tool evidence, and next steps when relevant. Do not return a single unformatted paragraph unless the user explicitly asks for a very short answer.\n \"\"\"\n\n init(baseURL: String) {\n self.baseURL = AgentAPIClient.normalizedBaseURL(baseURL)\n decoder = JSONDecoder()\n }\n\n func setBaseURL(_ value: String) {\n baseURL = AgentAPIClient.normalizedBaseURL(value, fallback: baseURL)\n }\n\n func health() async throws -> HealthPayload {\n try await get(\"health\")\n }\n\n func systemStatus() async throws -> JSONValue {\n try await get(\"system/status\")\n }\n\n func tools() async throws -> ToolCatalog {\n try await get(\"tools\")\n }\n\n func runs() async throws -> [RunItem] {\n try await get(\"runs?limit=30\")\n }\n\n func approvals() async throws -> [ApprovalItem] {\n try await get(\"approvals?status=pending&limit=30\")\n }\n\n func channels() async throws -> [ChannelInfo] {\n try await get(\"channels\")\n }\n\n func outbox() async throws -> OutboxEnvelope {\n try await get(\"channels/outbox\")\n }\n\n func channelStatus(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/status\", body: payload)\n }\n\n func channelDoctor(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/doctor\", body: payload)\n }\n\n func channelRequirements(channelID: String) async throws -> JSONValue {\n try await get(\"channels/requirements?channel_id=\\(Self.urlEncode(channelID))\")\n }\n\n func runChannelSmoke(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"prepare_messages\"] = true\n payload[\"dry_run_sends\"] = true\n return try await post(\"channels/smoke\", body: payload)\n }\n\n func channelListeners(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/listeners\", body: payload)\n }\n\n func tickChannelListener(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func tickAllChannelListeners(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func saveChannelSetup(channel: ChannelInfo, setup: ChannelSetup) async throws -> JSONValue {\n let payload: [String: Any] = [\n \"channel_id\": channel.channelId,\n \"enabled\": setup.enabled,\n \"listen_enabled\": setup.listenEnabled,\n \"conversation_defaults\": [\n \"conversation_id\": setup.conversationId,\n \"conversation_type\": setup.conversationType\n ],\n \"secret_refs\": channelSecretRefs(setup),\n \"secret_configured\": channelSecretConfigured(setup),\n \"allowlist\": cleanList(setup.allowlist),\n \"group_allowlist\": cleanList(setup.groupAllowlist),\n \"notes\": setup.notes\n ]\n return try await post(\"channels/setup\", body: payload)\n }\n\n func prepareChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Prepared from the macOS app channel panel.\"\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/prepare\", body: payload)\n }\n\n func sendChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Approval-gated send from the macOS app channel panel.\"\n payload[\"approve_high_risk\"] = settings.approveHighRisk\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/send\", body: payload)\n }\n\n func sendChannelInbound(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"conversation_type\"] = setup.conversationType.isEmpty ? \"dm\" : setup.conversationType\n payload[\"sender_id\"] = \"macos-app\"\n payload[\"text\"] = text\n payload[\"requires_response\"] = true\n payload[\"prepare_reply\"] = true\n return try await post(\"channels/inbound\", body: payload)\n }\n\n func voiceStatus(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n try await post(\"voice/status\", body: runtimePayload(settings: settings, secrets: secrets))\n }\n\n func autonomousStatus() async throws -> JSONValue {\n try await get(\"autonomous/status?limit=10\")\n }\n\n func runAutonomousCycle(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"max_cycles\"] = settings.maxCycles\n payload[\"idle_sleep_seconds\"] = 0\n payload[\"stop_after_idle_cycles\"] = 1\n payload[\"allow_initiative\"] = settings.allowInitiative\n return try await post(\"autonomous/cycles\", body: payload)\n }\n\n func sendStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> AgentRunResponse {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"text\"] = formattedRequestText(text)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": \"detailed\"\n ]\n return try await post(\"stimuli\", body: payload)\n }\n\n func streamStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) throws -> AsyncThrowingStream<AgentStreamEvent, Error> {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"text\"] = formattedRequestText(text)\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId,\n \"response_format\": \"markdown\",\n \"response_detail\": \"detailed\"\n ]\n\n var request = URLRequest(url: try routeURL(\"stimuli/stream\"))\n request.httpMethod = \"POST\"\n request.timeoutInterval = 300\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: payload, options: [])\n\n return AsyncThrowingStream { continuation in\n Task {\n do {\n let (bytes, response) = try await URLSession.shared.bytes(for: request)\n if let http = response as? HTTPURLResponse, !(200..<300).contains(http.statusCode) {\n throw AgentAPIError.requestFailed(\"stimuli/stream failed with HTTP \\(http.statusCode).\")\n }\n var parser = ServerSentEventParser()\n for try await line in bytes.lines {\n if let event = try parser.consume(line) {\n continuation.yield(event)\n }\n }\n if let event = try parser.finish() {\n continuation.yield(event)\n }\n continuation.finish()\n } catch {\n continuation.finish(throwing: error)\n }\n }\n }\n }\n\n func approve(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/approve\", body: [\"note\": note])\n }\n\n func reject(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/reject\", body: [\"note\": note])\n }\n\n func cancelRun(_ runID: String, reason: String) async throws -> JSONValue {\n try await post(\"runs/\\(runID)/cancel\", body: [\"reason\": reason])\n }\n\n func runTimeline(_ runID: String) async throws -> JSONValue {\n try await get(\"runs/\\(runID)/timeline?limit=100\")\n }\n\n private func get<T: Decodable>(_ route: String) async throws -> T {\n let request = URLRequest(url: try routeURL(route))\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func post<T: Decodable>(_ route: String, body: [String: Any]) async throws -> T {\n var request = URLRequest(url: try routeURL(route))\n request.httpMethod = \"POST\"\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: body, options: [])\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func routeURL(_ route: String) throws -> URL {\n guard let url = URL(string: route, relativeTo: baseURL)?.absoluteURL else {\n throw AgentAPIError.requestFailed(\"Invalid API route: \\(route)\")\n }\n return url\n }\n\n private func validate(response: URLResponse, data: Data) throws {\n guard let http = response as? HTTPURLResponse, (200..<300).contains(http.statusCode) else {\n let message = String(data: data, encoding: .utf8) ?? \"Request failed.\"\n throw AgentAPIError.requestFailed(message)\n }\n }\n\n private func formattedRequestText(_ text: String) -> String {\n let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)\n guard !trimmed.isEmpty else { return text }\n return \"\"\"\n \\(trimmed)\n\n Response formatting requirements:\n \\(responseFormattingInstruction)\n \"\"\"\n }\n\n private func runtimePayload(settings: AppSettings, secrets: RuntimeSecrets) -> [String: Any] {\n var payload: [String: Any] = [\n \"planner\": settings.planner,\n \"approve_high_risk\": settings.approveHighRisk,\n \"model_provider\": apiModelProvider(settings.modelProvider),\n \"model\": settings.modelName,\n \"model_api_key_env\": modelKeyName(settings.modelProvider)\n ]\n if !settings.modelBaseURL.isEmpty {\n payload[\"model_base_url\"] = settings.modelBaseURL\n }\n\n var secretPayload: [String: String] = [:]\n addSecret(&secretPayload, name: modelKeyName(settings.modelProvider), value: secrets.modelAPIKey)\n addSecret(&secretPayload, name: \"DEEPGRAM_API_KEY\", value: secrets.deepgramAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_API_KEY\", value: secrets.elevenLabsAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_VOICE_ID\", value: settings.voiceId)\n addSecret(&secretPayload, name: \"ELEVENLABS_MODEL_ID\", value: settings.elevenLabsModel)\n for channel in settings.channels {\n addSecret(&secretPayload, name: channel.secretName, value: channel.secretValue)\n for item in channel.secretValues {\n addSecret(&secretPayload, name: item.key, value: item.value)\n }\n }\n if !secretPayload.isEmpty {\n payload[\"runtime_secrets\"] = secretPayload\n }\n return payload\n }\n\n private func addSecret(_ secrets: inout [String: String], name: String, value: String) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n let cleanValue = value.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, !cleanValue.isEmpty {\n secrets[cleanName] = cleanValue\n }\n }\n\n private func modelKeyName(_ provider: String) -> String {\n switch provider {\n case \"groq\": \"GROQ_API_KEY\"\n case \"grok\": \"XAI_API_KEY\"\n case \"ollama\": \"OLLAMA_API_KEY\"\n case \"local-openai\": \"LOCAL_LLM_API_KEY\"\n default: \"OPENAI_API_KEY\"\n }\n }\n\n private func apiModelProvider(_ provider: String) -> String {\n provider == \"openai\" ? \"openai-responses\" : provider\n }\n\n private func channelSecretRefs(_ setup: ChannelSetup) -> [String: String] {\n var refs: [String: String] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n refs[\"primary\"] = primary\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n refs[cleanName] = cleanName\n }\n }\n return refs\n }\n\n private func channelSecretConfigured(_ setup: ChannelSetup) -> [String: Bool] {\n var configured: [String: Bool] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n configured[\"primary\"] = setup.secretConfigured || !setup.secretValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n configured[cleanName] = !setup.secretValues[cleanName, default: \"\"].trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n }\n return configured\n }\n\n private func cleanList(_ values: [String]) -> [String] {\n values.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }.filter { !$0.isEmpty }\n }\n\n private static func normalizedBaseURL(_ value: String, fallback: URL = URL(string: \"http://127.0.0.1:8765/\")!) -> URL {\n let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)\n .trimmingCharacters(in: CharacterSet(charactersIn: \"/\"))\n guard !trimmed.isEmpty, let url = URL(string: \"\\(trimmed)/\") else {\n return fallback\n }\n return url\n }\n\n private static func urlEncode(_ value: String) -> String {\n value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? value\n }\n}\n\nenum AgentAPIError: LocalizedError {\n case requestFailed(String)\n\n var errorDescription: String? {\n switch self {\n case .requestFailed(let message): message\n }\n }\n}\n\nprivate struct ServerSentEventParser {\n private var eventName = \"message\"\n private var dataLines: [String] = []\n private let decoder = JSONDecoder()\n\n mutating func consume(_ line: String) throws -> AgentStreamEvent? {\n if line.hasPrefix(\"event: \") {\n eventName = String(line.dropFirst(\"event: \".count)).trimmingCharacters(in: .whitespaces)\n return nil\n }\n if line.hasPrefix(\"data: \") {\n dataLines.append(String(line.dropFirst(\"data: \".count)))\n return try finish()\n }\n if line.isEmpty {\n return try finish()\n }\n return nil\n }\n\n mutating func finish() throws -> AgentStreamEvent? {\n guard !dataLines.isEmpty else { return nil }\n let data = Data(dataLines.joined(separator: \"\\n\").utf8)\n let value = try decoder.decode(JSONValue.self, from: data)\n let event = AgentStreamEvent(event: eventName, data: value)\n eventName = \"message\"\n dataLines = []\n return event\n }\n}\n"}, "51ad05013df285208a4bb212406d76ae6127a179": {"t": 1785756652.501214, "n": 14548, "h": "6d5d04aedb286ddb", "c": "import Foundation\n\nstruct RuntimeSecrets {\n var modelAPIKey = \"\"\n var deepgramAPIKey = \"\"\n var elevenLabsAPIKey = \"\"\n}\n\n@MainActor\nfinal class AgentAPIClient {\n private var baseURL: URL\n private let decoder: JSONDecoder\n private let encoder = JSONEncoder()\n\n init(baseURL: String) {\n self.baseURL = AgentAPIClient.normalizedBaseURL(baseURL)\n decoder = JSONDecoder()\n }\n\n func setBaseURL(_ value: String) {\n baseURL = AgentAPIClient.normalizedBaseURL(value, fallback: baseURL)\n }\n\n func health() async throws -> HealthPayload {\n try await get(\"health\")\n }\n\n func systemStatus() async throws -> JSONValue {\n try await get(\"system/status\")\n }\n\n func tools() async throws -> ToolCatalog {\n try await get(\"tools\")\n }\n\n func runs() async throws -> [RunItem] {\n try await get(\"runs?limit=30\")\n }\n\n func approvals() async throws -> [ApprovalItem] {\n try await get(\"approvals?status=pending&limit=30\")\n }\n\n func channels() async throws -> [ChannelInfo] {\n try await get(\"channels\")\n }\n\n func outbox() async throws -> OutboxEnvelope {\n try await get(\"channels/outbox\")\n }\n\n func channelStatus(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/status\", body: payload)\n }\n\n func channelDoctor(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/doctor\", body: payload)\n }\n\n func channelRequirements(channelID: String) async throws -> JSONValue {\n try await get(\"channels/requirements?channel_id=\\(Self.urlEncode(channelID))\")\n }\n\n func runChannelSmoke(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"prepare_messages\"] = true\n payload[\"dry_run_sends\"] = true\n return try await post(\"channels/smoke\", body: payload)\n }\n\n func channelListeners(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n return try await post(\"channels/listeners\", body: payload)\n }\n\n func tickChannelListener(channelID: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channelID\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func tickAllChannelListeners(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"limit\"] = 10\n payload[\"prepare_replies\"] = true\n return try await post(\"channels/listeners/tick\", body: payload)\n }\n\n func saveChannelSetup(channel: ChannelInfo, setup: ChannelSetup) async throws -> JSONValue {\n let payload: [String: Any] = [\n \"channel_id\": channel.channelId,\n \"enabled\": setup.enabled,\n \"listen_enabled\": setup.listenEnabled,\n \"conversation_defaults\": [\n \"conversation_id\": setup.conversationId,\n \"conversation_type\": setup.conversationType\n ],\n \"secret_refs\": channelSecretRefs(setup),\n \"secret_configured\": channelSecretConfigured(setup),\n \"allowlist\": cleanList(setup.allowlist),\n \"group_allowlist\": cleanList(setup.groupAllowlist),\n \"notes\": setup.notes\n ]\n return try await post(\"channels/setup\", body: payload)\n }\n\n func prepareChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Prepared from the macOS app channel panel.\"\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/prepare\", body: payload)\n }\n\n func sendChannelMessage(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"text\"] = text\n payload[\"reason\"] = \"Approval-gated send from the macOS app channel panel.\"\n payload[\"approve_high_risk\"] = settings.approveHighRisk\n payload[\"metadata\"] = [\n \"source\": \"macos_app\",\n \"conversation_type\": setup.conversationType\n ]\n return try await post(\"channels/message/send\", body: payload)\n }\n\n func sendChannelInbound(channel: ChannelInfo, setup: ChannelSetup, text: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"channel_id\"] = channel.channelId\n payload[\"conversation_id\"] = setup.conversationId.isEmpty ? \"mac-app-preview\" : setup.conversationId\n payload[\"conversation_type\"] = setup.conversationType.isEmpty ? \"dm\" : setup.conversationType\n payload[\"sender_id\"] = \"macos-app\"\n payload[\"text\"] = text\n payload[\"requires_response\"] = true\n payload[\"prepare_reply\"] = true\n return try await post(\"channels/inbound\", body: payload)\n }\n\n func voiceStatus(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n try await post(\"voice/status\", body: runtimePayload(settings: settings, secrets: secrets))\n }\n\n func autonomousStatus() async throws -> JSONValue {\n try await get(\"autonomous/status?limit=10\")\n }\n\n func runAutonomousCycle(settings: AppSettings, secrets: RuntimeSecrets) async throws -> JSONValue {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"max_cycles\"] = settings.maxCycles\n payload[\"idle_sleep_seconds\"] = 0\n payload[\"stop_after_idle_cycles\"] = 1\n payload[\"allow_initiative\"] = settings.allowInitiative\n return try await post(\"autonomous/cycles\", body: payload)\n }\n\n func sendStimulus(_ text: String, source: String, responseMode: String, settings: AppSettings, secrets: RuntimeSecrets) async throws -> AgentRunResponse {\n var payload = runtimePayload(settings: settings, secrets: secrets)\n payload[\"text\"] = text\n payload[\"source\"] = source\n payload[\"response_mode\"] = responseMode\n payload[\"metadata\"] = [\n \"response_mode\": responseMode,\n \"tts_provider\": settings.ttsProvider,\n \"voice_id\": settings.voiceId\n ]\n return try await post(\"stimuli\", body: payload)\n }\n\n func approve(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/approve\", body: [\"note\": note])\n }\n\n func reject(_ token: String, note: String) async throws -> JSONValue {\n try await post(\"approvals/\\(token)/reject\", body: [\"note\": note])\n }\n\n func cancelRun(_ runID: String, reason: String) async throws -> JSONValue {\n try await post(\"runs/\\(runID)/cancel\", body: [\"reason\": reason])\n }\n\n func runTimeline(_ runID: String) async throws -> JSONValue {\n try await get(\"runs/\\(runID)/timeline?limit=100\")\n }\n\n private func get<T: Decodable>(_ route: String) async throws -> T {\n let request = URLRequest(url: try routeURL(route))\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func post<T: Decodable>(_ route: String, body: [String: Any]) async throws -> T {\n var request = URLRequest(url: try routeURL(route))\n request.httpMethod = \"POST\"\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.httpBody = try JSONSerialization.data(withJSONObject: body, options: [])\n let (data, response) = try await URLSession.shared.data(for: request)\n try validate(response: response, data: data)\n return try decoder.decode(T.self, from: data)\n }\n\n private func routeURL(_ route: String) throws -> URL {\n guard let url = URL(string: route, relativeTo: baseURL)?.absoluteURL else {\n throw AgentAPIError.requestFailed(\"Invalid API route: \\(route)\")\n }\n return url\n }\n\n private func validate(response: URLResponse, data: Data) throws {\n guard let http = response as? HTTPURLResponse, (200..<300).contains(http.statusCode) else {\n let message = String(data: data, encoding: .utf8) ?? \"Request failed.\"\n throw AgentAPIError.requestFailed(message)\n }\n }\n\n private func runtimePayload(settings: AppSettings, secrets: RuntimeSecrets) -> [String: Any] {\n var payload: [String: Any] = [\n \"planner\": settings.planner,\n \"approve_high_risk\": settings.approveHighRisk,\n \"model_provider\": apiModelProvider(settings.modelProvider),\n \"model\": settings.modelName,\n \"model_api_key_env\": modelKeyName(settings.modelProvider)\n ]\n if !settings.modelBaseURL.isEmpty {\n payload[\"model_base_url\"] = settings.modelBaseURL\n }\n\n var secretPayload: [String: String] = [:]\n addSecret(&secretPayload, name: modelKeyName(settings.modelProvider), value: secrets.modelAPIKey)\n addSecret(&secretPayload, name: \"DEEPGRAM_API_KEY\", value: secrets.deepgramAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_API_KEY\", value: secrets.elevenLabsAPIKey)\n addSecret(&secretPayload, name: \"ELEVENLABS_VOICE_ID\", value: settings.voiceId)\n addSecret(&secretPayload, name: \"ELEVENLABS_MODEL_ID\", value: settings.elevenLabsModel)\n for channel in settings.channels {\n addSecret(&secretPayload, name: channel.secretName, value: channel.secretValue)\n for item in channel.secretValues {\n addSecret(&secretPayload, name: item.key, value: item.value)\n }\n }\n if !secretPayload.isEmpty {\n payload[\"runtime_secrets\"] = secretPayload\n }\n return payload\n }\n\n private func addSecret(_ secrets: inout [String: String], name: String, value: String) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n let cleanValue = value.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, !cleanValue.isEmpty {\n secrets[cleanName] = cleanValue\n }\n }\n\n private func modelKeyName(_ provider: String) -> String {\n switch provider {\n case \"groq\": \"GROQ_API_KEY\"\n case \"grok\": \"XAI_API_KEY\"\n case \"ollama\": \"OLLAMA_API_KEY\"\n case \"local-openai\": \"LOCAL_LLM_API_KEY\"\n default: \"OPENAI_API_KEY\"\n }\n }\n\n private func apiModelProvider(_ provider: String) -> String {\n provider == \"openai\" ? \"openai-responses\" : provider\n }\n\n private func channelSecretRefs(_ setup: ChannelSetup) -> [String: String] {\n var refs: [String: String] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n refs[\"primary\"] = primary\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n refs[cleanName] = cleanName\n }\n }\n return refs\n }\n\n private func channelSecretConfigured(_ setup: ChannelSetup) -> [String: Bool] {\n var configured: [String: Bool] = [:]\n let primary = setup.secretName.trimmingCharacters(in: .whitespacesAndNewlines)\n if !primary.isEmpty {\n configured[\"primary\"] = setup.secretConfigured || !setup.secretValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n for name in setup.secretNames + Array(setup.secretValues.keys) {\n let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)\n if !cleanName.isEmpty, cleanName.localizedCaseInsensitiveCompare(primary) != .orderedSame {\n configured[cleanName] = !setup.secretValues[cleanName, default: \"\"].trimmingCharacters(in: .whitespacesAndNewlines).isEmpty\n }\n }\n return configured\n }\n\n private func cleanList(_ values: [String]) -> [String] {\n values.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }.filter { !$0.isEmpty }\n }\n\n private static func normalizedBaseURL(_ value: String, fallback: URL = URL(string: \"http://127.0.0.1:8765/\")!) -> URL {\n let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)\n .trimmingCharacters(in: CharacterSet(charactersIn: \"/\"))\n guard !trimmed.isEmpty, let url = URL(string: \"\\(trimmed)/\") else {\n return fallback\n }\n return url\n }\n\n private static func urlEncode(_ value: String) -> String {\n value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? value\n }\n}\n\nenum AgentAPIError: LocalizedError {\n case requestFailed(String)\n\n var errorDescription: String? {\n switch self {\n case .requestFailed(let message): message\n }\n }\n}\n"}}}}