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>
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>
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>
1. Vision SSRF (#2878, #2872):
Add _validate_url_safe() that resolves the target hostname via DNS and
rejects any IP in private (RFC1918), loopback, link-local, or reserved
ranges before requests.get() is called. This blocks attacks that use
attacker-controlled image URLs to probe internal services or cloud
metadata endpoints (169.254.169.254).
2. Skill install path traversal (#2873):
Add _safe_skill_dir() that validates the skill name cannot escape the
skills/ root directory. Rejects names containing '..', absolute paths,
and any resolved path that falls outside the custom_dir boundary.
Applied to _add_url(), _add_package(), and delete().
Both fixes include comprehensive unit tests (19 test cases) covering
blocked patterns, edge cases, and allowed legitimate usage.
Closes#2878Closes#2873
Ref: #2872
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).
- Add language labels and copy buttons to code blocks
- Enhance drag-and-drop to full chat view
- Fix data consistency bugs in message operations
- Use RLock to prevent deadlock in conversation store"
Replace the broad `~/.cow` directory check with a regex that matches
only the credential file path (`\.cow[/\\]\.env`), so legitimate access
to other `~/.cow/` subdirectories (e.g. skills) is no longer blocked.
Drop the incomplete env/printenv blocking rule per reviewer feedback.
Rewrite test_invariant_bash.py to use the correct Bash().execute()
API and cover both the blocked and allowed cases.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
_stdio_send (single pipe) must remain serialized under _call_lock,
but SSE and streamable-http use independent HTTP requests and can
safely execute concurrently across sessions.
- Scope _call_lock to stdio transport only
- Add _http_lock with double-checked pattern to protect _http_session_id
initialization during concurrent streamable-http requests
- Use reader thread + queue.Queue instead of select.select() which does not
work with pipes on Windows (only sockets)
- Make MCP server timeout configurable via mcp.json (default 120s)
- Validate JSON-RPC response id to skip stale responses from timed-out calls
- Log MCP server stderr at WARNING level instead of DEBUG for visibility