Skip to content

What Changed

Choose a tag to compare

@chauncygu chauncygu released this 07 May 21:32
· 177 commits to main since this release
530dbe7
  • 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)