diff --git a/backend/CLAUDE.md b/backend/CLAUDE.md index 65817284..6c5873eb 100644 --- a/backend/CLAUDE.md +++ b/backend/CLAUDE.md @@ -59,4 +59,4 @@ What runs under `bash backend/run.sh` is not what ships in the DMG/EXE. Test the - **Compaction must actually trim, not just mark.** `_build_history_prefix` honors `session.compacted_through_msg_id`; the auto path leaves the SDK session intact (preserves prompt cache, the ~70% aux-cost win), the manual `/compact` button sets `needs_fresh_session=True` because the user is opting in to the cache-loss tradeoff for a visible shrink. Never flatten to `User:/Assistant:` text and never throw away tool turns; the model loses fidelity if you do. - **SSRF guard is async + multi-record.** `tools/ssrf_guard.py` uses `loop.getaddrinfo` (non-blocking, covers IPv4 + IPv6) and rejects if ANY resolved record is private. Loopback (`127/8`, `::1`) is intentionally ALLOWED because App Builder previews on `127.0.0.1:`; the real desktop-app SSRF threat is cloud metadata (`169.254.169.254`) and corporate LAN, not localhost. Per-redirect re-validation via `safe_fetch`; never use `follow_redirects=True` directly in a fetch path that takes a user-supplied URL. - **Inline-text attachments need a combined cap, not just per-file.** `prompt/attachments.py` tracks `text_total_chars` so a pile of `.txt` files can't silently blow the context window past the per-file 512KB. Picked 1.5M chars as a 1M-window-model-friendly default. -- **`ENABLE_TOOL_SEARCH=1` (not `auto`) on non-Anthropic paths.** Forces tool schemas to load on demand instead of upfront — saves ~9K first-message tokens. The CLI's `tengu_defer_all_bn4` Statsig flag still defers tools without the env set, so a non-Anthropic path without this loses tools entirely. +- **`ENABLE_TOOL_SEARCH=auto`, NOT `1`, on the 9Router/sub paths.** We briefly forced `1` (to save ~9K first-message tokens) and it 400'd the Claude subscription route: forcing `1` marks every tool `defer_loading=true`, and Anthropic rejects `defer_loading=true` + `cache_control` on the same tool (our prompt-cache flip sets `cache_control`). `auto` eager-loads tools when they fit the schema budget (no defer flag, no collision) and only defers when needed, which is exactly what we want; the bare default is what's unsafe (CLI's `tengu_defer_all_bn4` defers 16 tools with no way to load them). So: `auto` everywhere on these paths, never `1`. diff --git a/backend/apps/agents/agent_manager.py b/backend/apps/agents/agent_manager.py index ee438789..fe4cd393 100644 --- a/backend/apps/agents/agent_manager.py +++ b/backend/apps/agents/agent_manager.py @@ -1530,7 +1530,7 @@ class AgentManager: env = { "ANTHROPIC_API_KEY": "9router", "ANTHROPIC_BASE_URL": "http://localhost:20128", - "ENABLE_TOOL_SEARCH": "1", + "ENABLE_TOOL_SEARCH": "auto", } if cp: # Local OpenAI-compatible servers (LM Studio, Ollama, ...) @@ -1600,7 +1600,7 @@ class AgentManager: env["CLAUDE_CODE_SUBAGENT_MODEL"] = "openrouter/anthropic/claude-sonnet-4.5" env["ANTHROPIC_SMALL_FAST_MODEL"] = "openrouter/anthropic/claude-haiku-4.5" env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = "openrouter/anthropic/claude-haiku-4.5" - env["ENABLE_TOOL_SEARCH"] = "1" + env["ENABLE_TOOL_SEARCH"] = "auto" options_kwargs["env"] = env logger.info(f"[MCP-DEBUG] Using OpenRouter for {session.model}") elif api_type == "anthropic" and not resolved_is_9router and getattr(global_settings, "connection_mode", "own_key") == "openswarm-pro": @@ -1676,7 +1676,7 @@ class AgentManager: # Anthropic networks. "auto" eagerly loads tools when schema # budget fits in ~10% of context. Don't pass --bare, sets # CLAUDE_CODE_SIMPLE=1 which strips the system prompt scaffolding. - env["ENABLE_TOOL_SEARCH"] = "1" + env["ENABLE_TOOL_SEARCH"] = "auto" options_kwargs["env"] = env logger.info(f"[MCP-DEBUG] Using 9Router (api_type={api_type})") else: