- Auto-rebuild knowledge/index.md from the real directory tree on
create/import so it never drifts or loses documents (no longer relies
on the agent hand-writing it).
- Auto-open the created/imported document in the tree after success.
- Add create_document status message, shorten EN action buttons, and
localize the "insert template" content.
- Show filename for protected system files (index.md/log.md) in the tree
instead of their H1 heading.
- Reuse a shared embedding-provider factory so knowledge index sync also
gets vectors instead of degrading to keyword-only search.
Introduce get_data_root() driven by the COW_DATA_DIR env var so the
packaged desktop build stores config.json, run.log, user data and
WeChat credentials under ~/.cow — surviving app updates and keeping the
app bundle read-only. Source deployments leave COW_DATA_DIR unset and
fall back to the repo root, so existing behavior is unchanged.
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
- Remove vendor-specific preset model lists for custom embedding/vision
providers; users now type the model id manually (lists vary per vendor).
- Strip third-party vendor names from comments.
- Fix AttributeError in _set_vision/_set_embedding: the provider constants
live on ModelsHandler, not ConfigHandler, so selecting a built-in
provider crashed. Also replace the fragile [:-1] slice with an explicit
non-custom filter.
Co-authored-by: Cursor <cursoragent@cursor.com>
1. Embedding model: support custom provider
- Add "custom" entry to EMBEDDING_VENDORS with supports_dim_param=False
- Parse custom:<id> credentials and model fallback in agent_initializer
- Expand custom_providers as custom:<id> entries in Web UI dropdown
2. Vision model: support custom provider
- Add custom:<id> routing in _route_by_provider_id
- Add _build_custom_provider reading credentials from custom_providers
- Expand custom_providers in Web UI dropdown, add validation in _set_vision
3. Fix memory_get Windows path validation bug!
- str.startswith(path+'/') always False on Windows due to backslashes, So All Users can not use "memory_get" tool in Windows.
- Use os.path.realpath + os.sep, consistent with MemoryService
4. Fix historical needsModel:false bug preventing embedding provider switch
- Change embedding needsModel to true in console.js
- Support custom:<id> resolution in cow_cli /memory status, also for adding custom provider support
Closes#2908, Closes#2880
- Add Kimi kimi-k2.7-code (default), kimi-k2.7-code-highspeed, and GLM glm-5.2 (default)
- Fix 400 error when disabling thinking on kimi-k2.7-code; omit the thinking param for this series since it only accepts type=enabled
- Update README, docs (zh/en/ja), install scripts, and Web console model dropdown
Replace the boolean wecom_bot_callback with a wecom_bot_mode field
("websocket" | "webhook"), consistent with the Feishu channel's
feishu_event_mode. Update startup() and send() to branch on the mode.
Also fix a temp-file leak in _load_image_base64: downloads, format
conversions and compressions wrote to /tmp but were never removed. Track
only the temp files created here and delete them in a finally block,
leaving the caller's original local file untouched.
Co-authored-by: Cursor <cursoragent@cursor.com>
Remove the callback-mode fields (Callback Mode / Token / EncodingAESKey /
Port) from the web console channel form; these are rarely changed and are
now configured via config.json only. The console keeps Bot ID / Secret for
the long-connection setup.
Serve the callback HTTP server on a fixed path (/wecombot) instead of any
path (/.*), so unrelated requests 404 rather than being processed as
signature-failing WeCom callbacks. The bot's receive-message URL must point
at http(s)://host:<port>/wecombot.
Co-authored-by: Cursor <cursoragent@cursor.com>
The 2MB cap (matching the long-connection upload path) does not work for
the callback path: there the whole image is base64-embedded in an
AES-encrypted body returned on every poll. A ~1.5MB image (base64 ~2.1MB,
encrypted ~2.8MB) makes WeCom reject the finish packet and poll forever,
which also surfaces as a truncated text bubble and WeCom's own timeout
error. Cap well below that at 512KB so the finish packet is accepted.
Co-authored-by: Cursor <cursoragent@cursor.com>
Defer the callback stream finish after a text reply so a trailing
image-with-caption send (text first, image 0.3s later) can merge in
instead of closing the stream prematurely. Raise the callback inline
image cap from 512KB to 2MB to match the long-connection upload path.
Co-authored-by: Cursor <cursoragent@cursor.com>
In callback mode the image is base64-embedded in the stream finish reply and
the whole response is AES-encrypted and returned on every poll. A multi-MB
body is rejected/times out on WeCom's side, leaving the "···" bubble spinning
and the image never shown.
- Compress callback images to <=512KB (JPEG, resize if needed) instead of the
10MB the protocol nominally allows
- Fall back to the original image if compression fails, and log the final
base64 payload size for diagnosis
Co-authored-by: Cursor <cursoragent@cursor.com>
The streaming "···" bubble should only stop when the task actually completes
or the user cancels — not on an arbitrary timeout that would also steal the
response_url fallback's chance to deliver a late answer.
- On agent_cancelled, finalize the stream (finish=true, "🛑 已中止" if empty)
and schedule the response_url fallback so the bubble clears immediately when
a run is cancelled, even past the poll window
- Do not force-finish a still-running stream on a timer; let it keep spinning
until completion or cancel. Answers that finish after WeCom's ~6min poll
window are delivered via response_url instead
Co-authored-by: Cursor <cursoragent@cursor.com>
- Add toggle, update and delete APIs for scheduled tasks
- Add task edit modal with schedule/action updates in web console (PS: In edit box, now I Prevent channel type changes during editing (weixin token bound to session) )
- Add enable/disable switch with visual feedback in task cards
- Sort task list by enabled status first, then by next_run_at
Closes#2882
Handle agent replies that finish after WeCom stops polling the passive
stream (the poll window is ~6min from the user's message).
- Capture response_url from the message callback and, when a reply is
finalized but no poll picks it up within a short grace period, push it as
a one-shot active markdown reply (valid 1h, single use)
- Guard against double delivery via delivered/url_sent flags
- Embed public image URLs in the active markdown; note when a local image
can't be delivered post-timeout
- Append (instead of discarding) the unsupported-type notice for
file/voice/video replies so streamed text is preserved
- Quiet the per-poll debug log and log stream completion with content size
Co-authored-by: Cursor <cursoragent@cursor.com>
- Merge the separate custom-providers section into the unified provider
grid; "Custom" in the add-provider picker now acts as an add-new action
(trailing + mark) and opens the dedicated modal, supporting multiple
OpenAI-compatible endpoints
- Simplify the custom provider modal: drop the default-model field, add
an inline delete button, align colors with the theme
- Keep the legacy single "custom" card visible (models page, chat
dropdown and legacy config page) while custom_api_key/custom_api_base
is still in use, so existing single-provider setups don't disappear
- Unify user-facing wording from "vendor" to "provider" in UI and docs
- Restructure custom provider docs (zh/en/ja) around Web console and
config file usage
Support receiving WeCom smart-bot messages via encrypted HTTP callback in
addition to the existing WebSocket long connection. Disabled by default
(wecom_bot_callback=false).
- Add wecom_bot_callback / wecom_bot_token / wecom_bot_encoding_aes_key /
wecom_bot_port config keys
- Add WXBizJsonMsgCrypt-based crypto module for URL verification, callback
decryption and passive-reply encryption (receive_id empty for internal bots)
- Reply asynchronously via the official stream-refresh polling: register a
stream id on first reply, accumulate agent output into per-stream state, and
serve the latest content (text + image) on each poll until finish
- Fall back to EncodingAESKey for media decryption when callback bodies carry
no per-message aeskey
- Degrade unsupported passive replies (file/voice/video) to a text notice
- Expose the new fields in the Web console channel config
Co-authored-by: Cursor <cursoragent@cursor.com>
_set_chat rejected the expanded "custom:<id>" ids with "unknown
provider", so switching to a custom provider was only possible from
the custom providers section. Now the chat card and the custom section
behave consistently: _set_chat validates the id against
custom_providers, falls back to the provider's default model when none
is picked, and _chat_capability expands the dropdown with the
"custom:<id>" entries (legacy single-custom mode unchanged).
Co-authored-by: Cursor <cursoragent@cursor.com>
Two independent robustness fixes:
1. ChatChannel.cancel_session / cancel_all_session raised KeyError when a
session existed in self.sessions but no future had been dispatched yet.
self.sessions[sid] is created in produce(), but self.futures[sid] is only
created later in consume() on first dispatch. Cancelling in that window
(e.g. user sends a message then immediately cancels) crashed the cancel
path. Use self.futures.get(sid, []) so an absent entry is a no-op.
2. compress_imgfile decremented JPEG quality by 5 with no lower bound. For an
image that cannot be compressed below max_size, quality went 0, negative,
... — the loop never terminated and passed invalid quality values to PIL.
Add a min_quality floor (10) and return the best effort once reached.
Adds tests/test_robustness_fixes.py covering both paths (5 tests).
1. Creating a provider no longer auto-switches bot_type. Only an
explicit make_active=true changes the active model — prevents
silently hijacking users on Claude/OpenAI/etc.
2. When a provider IS activated, its 'model' is now written into the
global 'model' field. This ensures all three paths (regular chat,
agent_bridge, vision) use the correct model without per-path patches.
3. Removed unused i18n key 'models_custom_name_exists' (no longer
referenced after the id-based rework removed name-collision checks).
4. Updated tests: 39 passing (added model-sync tests, fixed tests that
relied on the removed auto-activation behavior).
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 a self-evolution subsystem that reviews idle conversations in an
isolated agent and durably learns from them — patching/creating skills,
finishing unfinished tasks, and backfilling missed memory.
- Trigger: background idle scan, fires when a session is idle >= N min AND
(>= N turns OR context usage > 80%). In-memory cursor reviews only new
messages so a session never re-learns old content.
- Isolated review agent: same model, restricted toolset, hard write-guard
confining edits to the workspace (built-in skills are protected).
- Safety: file-level backup before edits + evolution_undo tool; notify the
user ONLY when a workspace file actually changed (no-nag rule); capped
concurrency.
- Records to memory/evolution/<date>.md, surfaced in the memory UI's
renamed "Self-Evolution" tab (merged with dream diaries).
- Hide internal [SCHEDULED]/[EVOLUTION]/backup_id markers from chat history
display (also fixes scheduler marker leakage) while keeping them in stored
content for undo.
- Flat config: self_evolution_enabled (default off until release),
self_evolution_idle_minutes (15), self_evolution_min_turns (6).
- Tests: tests/test_evolution.py (stub + real model modes, 7 scenarios).