You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
May 7, 2026 (v3.05.77): MCP HTTP/SSE transport + OAuth 2.0 PKCE, .env loader, ANTHROPIC_ENDPOINT corporate-proxy override, AskUserQuestion UI polish (#88, #89) — cc_mcp/client.py now speaks Streamable HTTP (POST → text/event-stream reply) in addition to stdio and pure SSE, with the Accept: application/json, text/event-stream header servers like sap-jira require to stop 406-ing. OAuth 2.0: new cc_mcp/oauth.py implements the full MCP Authorization spec — RFC 9728 resource-server discovery → RFC 8414 AS metadata → RFC 7591 dynamic client registration → Authorization Code + PKCE (S256) flow with browser redirect → automatic refresh-token rotation. Tokens persist atomically to ~/.cheetahclaws/mcp_oauth.json at mode 0600 with the parent directory locked to 0700. The redirect-URI port is picked once and reused for both registration and the local callback server, the OAuth scope is sourced from the AS's advertised scopes_supported (preferring mcp if listed, otherwise the first one, otherwise omitted entirely so servers without an mcp scope no longer reject with invalid_scope), and _ensure_oauth() is guarded by a dedicated lock so concurrent 401-retries can't race on the httpx client rebuild. REPL: /mcp add <name> --transport http <url> and /mcp add <name> --transport sse <url> for one-line HTTP server registration; explicit /mcp list subcommand with full-width tool descriptions wrapped at 72 cols. Server name sanitization: hyphenated names like github-tools now resolve correctly through the mcp__server__tool qualified-name path. .env loader: _load_env() runs at the very top of cheetahclaws.py before any other import reads os.environ, so .env keys are visible to every module without losing existing-shell-var precedence (os.environ.setdefault). MCP HTTP headers values are passed through os.path.expandvars, so "Authorization": "Bearer $GITHUB_TOKEN" works out of the box. ANTHROPIC_ENDPOINT env var (also reachable via .env) overrides the persisted anthropic_endpoint config and is used by both the streaming Anthropic client (providers.py passes base_url=... to anthropic.Anthropic) and the connectivity probes in /doctor / setup wizard, letting corporate proxies swap api.anthropic.com cleanly. UI: AskUserQuestion is auto-approved alongside EnterPlanMode/ExitPlanMode (it's an interactive tool by definition, a permission prompt was redundant), the spinner and result line are suppressed in print_tool_start/end, the question text is rendered through clr() with Markdown stripped (**bold**, `code`, *italic*), and option indices/descriptions are colorized. The REPL prompt now prints a full-width ─ rule via os.get_terminal_size() (80-char fallback) before each input, matching Claude Code's visual rhythm.**
May 5, 2026: Telegram bridge file round-trip + cross-channel pickable permission prompts (#84) — bridges/telegram.py previously only had _tg_send (text via sendMessage), so when the model claimed it had "sent a file" it was just text and the [approve][reject] text in permission prompts only looked like buttons. Added _tg_send_document (multipart/form-data upload, 49 MB cap with explicit oversize/empty/missing/network/API-rejection error reporting), an inbound document handler that saves uploads to /workspace (or tempfile.gettempdir() outside Docker) with sanitized filenames and a path-aware prompt, a !sendfile <path> user command for explicit on-demand sends, and an auto-send hook in _bg_runner that mails any file written by the Write tool — FIFO-paired with the in-flight file_path, skipped on Error: / Denied: results, and de-duplicated per turn so parallel writes don't double-mail. Cross-channel permission UX: ask_input_interactive(options=[(label, value), …]) now renders an interactive picker on every bridge — Telegram gets a real inline_keyboard (callback_data="cc:<prompt_id>:<value>", _handle_callback_query does auth + stale-prompt-id drop + answerCallbackQuery + editMessageText "✓ Selected: y"), Slack and WeChat get a numbered menu in the message body (reply with digit / canonical letter / label word — all resolve via _resolve_choice), terminal prints the same numbered menu before the input cursor; ask_permission_interactive passes [(✅ Approve, y), (❌ Reject, n), (✅✅ Accept all, a)]. Backward-compatible: every existing ask_input_interactive call site (no options=) keeps free-text behavior. 49 new pytest cases (tests/test_telegram_bridge.py + tests/test_options_menu.py) — no real network calls. 718 passed, zero regressions on the 669 pre-existing. --accept-all was a red herring; the bridge simply lacked the upload code path.
May 2, 2026: Docker chat UI assets 404 follow-up (#73) — web/server.py now resolves _WEB_DIR via importlib.resources.files("web") instead of Path(__file__).parent, so static files are found whether the package is installed editable or non-editable. The dotfile guard in the static-file branch now only inspects path segments inside _WEB_DIR, so installs sitting under .venv/, .local/, etc. no longer 404 every asset. [tool.setuptools.package-data] for web widened to static/**/* so non-editable wheels reliably ship the full web/static/ subtree. Plus a new docs/guides/docker.md "Custom Dockerfile pitfalls" section covering the editable-install requirement and the most common 404 root cause for users rolling their own image.
Apr 30, 2026: Docker / home-server support (#73) — Dockerfile, docker-compose.yml, .env.example, host Ollama via host.docker.internal, workspace bind-mount for Samba sharing. --web mode now auto-starts configured Telegram / WeChat / Slack bridges in the same process so a single container delivers browser UI + phone bridge. Plus two terminal/agent fixes: AskUserQuestion no longer deadlocks the terminal (#69) — synchronous render+read instead of a queue/event the agent thread can't drain. messages_to_openai emits content: "" instead of null for tool-only assistant turns so Ollama's OpenAI-compat endpoint stops 400-ing with invalid message content type: <nil>; 400 / BadRequestError reclassified as a non-retryable INVALID_REQUEST so a malformed body no longer trips the circuit breaker (#71).
Apr 24, 2026: Support Deepseek V4 models, multi-model prompt adaptation — single shared default.md baseline + tiny per-family overlays (Anthropic XML tags · Gemini 3 explicit Agentic Mode · OpenAI o-series no-narration). Routing is by model family, not provider/runtime — same Qwen prompt whether served via DashScope, Ollama, or OpenRouter. Overlays must cite a vendor prompting guide (≤ 20 lines, enforced by tests). DeepSeek v4 thinking-mode protocol (reasoning_content round-trip + thinking: ON by default). fix(setup-wizard): tolerate api_key_env=None for ollama/lmstudio (#59)