Rework the multi custom-provider design per maintainer review:
1. Data model: use server-generated uuid4 short id as primary key;
'name' is now a pure display label that can be freely renamed.
2. Routing: drop 'custom_active_provider'; activate a provider by
setting bot_type to 'custom:<id>'. Single source of truth — no
pointer drift between bot_type and a separate active selector.
3. Security: drag_sensitive() now recursively masks api_key/secret in
nested structures (custom_providers list); previously only top-level
string fields were masked.
4. Per-provider model: the provider's 'model' field now takes effect on
the main chat path and agent path (was silently ignored before).
5. XSS fix: replace all inline onclick handlers in custom-provider UI
with data-* attributes + event delegation. Provider names never
appear in executable HTML contexts.
Legacy compatibility: bot_type='custom' (no colon) still reads the flat
custom_api_key/custom_api_base fields byte-for-byte identically.
Closes: consolidates #2876 into this PR as requested.
Ref: #2838
Adds first-class support for configuring more than one custom
OpenAI-compatible provider (e.g. SiliconFlow, DeepSeek, local vLLM)
and switching the active one from the web console, addressing #2838.
Backend:
- config: new `custom_providers` (list) and `custom_active_provider`
fields, fully backward compatible with the legacy single
`open_ai_api_base`/`model` fields (used as fallback).
- models/custom_provider.py: centralized resolver
`resolve_custom_credentials()` returning (api_key, api_base, model),
with active-provider selection and graceful fallback.
- chat_gpt_bot.py wired to use the resolver.
- web_channel.py: `_provider_overview` expands `custom_providers` into
one card per provider (id `custom:<name>`, active flag, masked key);
new POST actions `set_custom_provider`, `delete_custom_provider`,
`set_active_custom_provider` with hermetic persistence + bridge reset.
Frontend:
- console.js: dedicated "Custom providers" section with add / edit /
delete / set-active actions, masked-key keep-existing handling, and
~20 new zh/en i18n strings.
- chat.html: custom provider modal.
Tests:
- tests/test_custom_provider.py (11) - resolver/config behavior.
- tests/test_custom_provider_handlers.py (18) - write-side handlers and
overview expansion, including duplicate-name rejection.
All 29 unit tests pass.
- Add MINIMAX_M3 = "MiniMax-M3" constant and put it first in MODEL_LIST
- Default MinimaxBot model: MiniMax-M2.7 -> MiniMax-M3
- Keep MiniMax-M2.7 and MiniMax-M2.7-highspeed as legacy options
- Drop MINIMAX_M2_5 / MINIMAX_M2_1 / MINIMAX_M2_1_LIGHTNING / MINIMAX_M2
- Update web console recommended/provider model lists
- Update README capability table and docs/models index (en/zh/ja)
- Update docs/models/minimax.mdx and coding-plan.mdx MiniMax section
- Update run.sh / run.ps1 installer default and menu hint
- Update zh CLI status sample output
- Update unit tests to assert new M3 default and constant
TTS (speech-2.*) and API base URL remain unchanged.
Rename the WeCom customer-service channel and give it its own corp_id
field so users no longer have to share `wechatcom_corp_id` with the
self-built WeCom app channel.
Renames (channel-side):
- channel type / const: wechatcom_kf -> wechat_kf
- package dir: channel/wechatcom_kf/ -> channel/wechat_kf/
- python files / classes: WechatComKf* -> WechatKf*
- config keys: wechatcom_kf_{secret,token,aes_key,port} ->
wechat_kf_{secret,token,aes_key,port}; new wechat_kf_corp_id
- env vars: WECHATCOM_KF_* -> WECHAT_KF_*; new WECHAT_KF_CORP_ID
- log prefix / cursor file: [wechatcom_kf] -> [wechat_kf]
- web console CHANNEL_DEFS key + startup log line
Renames (docs):
- docs/channels/wecom-kf.mdx -> docs/channels/wechat-kf.mdx (zh/en/ja)
- update docs.json sidebar entries and all field names inside the docs
In addition, the Web Console "微信客服" entry now exposes its own
Corp ID field instead of reusing the wechatcom_app one, and includes
the screenshot of the visual config in the channel guide.
Web Console onboarding section is added (Tabs: Web Console / config
file) and the local URL `http://127.0.0.1:9899/` parenthetical is
dropped for consistency with other channel docs.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add a self-deployment guide for the new `wechatcom_kf` channel under
`docs/channels/wecom-kf.mdx` in zh / en / ja, mirroring the existing
`wecom.mdx` structure. Wire each language version into the sidebar in
`docs/docs.json`.
Walks through: creating the WeCom custom app, retrieving Corp ID /
Secret (push-to-phone) / Token / EncodingAESKey, configuring `config.json`,
saving the callback URL + Enterprise Trusted IPs, binding the WeCom
Customer Service account, and distributing the access link / QR code.
Co-authored-by: Cursor <cursoragent@cursor.com>
Browser sessions now reuse a Chromium user profile across runs by default
(`~/.cow/browser_profile`), so users only log in to a site once.
Three launch modes are selectable via `tools.browser` in config.json:
- persistent (default): Playwright Chromium with a persistent user_data_dir
- cdp: attach to an externally launched real Chrome via `cdp_endpoint`
(full fingerprints, ideal for sites with strict bot detection)
- fresh: clean context every run, set `persistent: false`
Also:
- Self-heal when the user closes the browser window mid-session: detect
closed page/context/browser via close listeners and exception scanning,
then transparently relaunch on the next request.
- Graceful CDP shutdown: disconnect only, never kill the user's Chrome.
- Friendly errors when the CDP endpoint is unreachable or the persistent
profile is locked, so the LLM can guide the user instead of looping.
- Fix tool config being silently overwritten by workspace config in
AgentInitializer; per-tool user settings (e.g. browser.cdp_endpoint)
are now merged instead of replaced.
- Update zh / en / ja docs with the new login-persistence section,
including the Chrome 137+ requirement to pair --remote-debugging-port
with a dedicated --user-data-dir.