The uniqueness guard counted occurrences via normalize_for_fuzzy_match,
while fuzzy_find_text located matches with a whitespace-flexible regex,
so the two could disagree. Extract the pattern builder as a single
source of truth (_build_fuzzy_pattern) and add count_matches, which
counts with the same exact-then-fuzzy strategy used to locate and
replace. This is the optional follow-up suggested in the review of #2942.
Adds regression tests for exact and fuzzy multi-match rejection.
The fuzzy fallback anchored every line with a leading [ \t]* that
greedily consumed the file's indentation into the matched region, so a
no-indent oldText dropped the edited line's indentation. Fold leading
whitespace into the match only when oldText was itself indented on the
first line, mirroring exact-substring semantics. Add a regression test.
The Read tool documents that a negative offset reads from the end (-N = last N lines). Content is split on newline, so a file ending in a newline produces a trailing empty element and total_file_lines is one too high. Every negative offset was therefore off by one: offset=-1 returned the empty string after the final newline instead of the last line, and -N returned N-1 real lines.
Exclude the trailing empty element when computing the start line for negative offsets. Adds regression tests.
Co-authored-by: Cursor <cursoragent@cursor.com>
The Edit tool falls back to a whitespace-tolerant fuzzy match when oldText does not match byte-for-byte. On a fuzzy hit it replaced text inside a whitespace-normalized copy of the whole file and wrote that copy back, so every untouched line lost its original indentation (runs of spaces/tabs collapsed to a single space). For indentation-sensitive files such as Python this silently corrupts the file.
Locate the fuzzy match in the original content with a whitespace-flexible regex and return offsets into that original content, so only the matched region is replaced. Adds regression tests.
Co-authored-by: Cursor <cursoragent@cursor.com>
When a command's output exceeds DEFAULT_MAX_BYTES the Bash tool spills the full output to a temp file. The file was opened in text mode without an explicit encoding, so it used the platform locale encoding (cp936/GBK on Chinese Windows). Output containing emoji or other characters not representable in that codepage raised UnicodeEncodeError, which propagated out and turned a successful command (exit code 0) into a tool error, discarding all output.
Open the temp file with encoding='utf-8', matching the sibling temp file written in _rewrite_long_python_c. Adds a regression test.
Co-authored-by: Cursor <cursoragent@cursor.com>
- 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.
# 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>
The browser tool navigates to a model-supplied URL via Playwright
page.goto and then auto-snapshots the page back to the model. The
http(s) navigate path performed no filtering, so a model tool call
(including under prompt injection) could point the agent-driven browser
at the cloud-metadata endpoint (169.254.169.254) and read the
credentials back through the snapshot.
Unlike the vision/web_fetch tools, the browser legitimately needs local
pages — a dev server on localhost / 127.0.0.1 / a LAN IP — so a blanket
"block all internal" policy is the wrong default here. The guard is
therefore narrow: it resolves the hostname and rejects only link-local
addresses (169.254.0.0/16, which includes the 169.254.169.254 metadata
endpoint, and IPv6 fe80::/10) plus the AWS IPv6 IMDS address
(fd00:ec2::254), before navigation. Loopback and RFC1918/LAN stay
reachable so local dev works out of the box.
Only http/https targets are validated; the documented non-HTTP scheme
handling (about:/data:/file:) is unchanged. An operator who deliberately
needs the link-local/metadata target can opt out with
tools.browser.allow_private_targets = true.
tests/test_security_ssrf_browser_navigate.py asserts link-local/metadata
targets are blocked while loopback, RFC1918/LAN and public targets
navigate through (browser service and DNS are stubbed; no real
browser/network).
Signed-off-by: christop <825583681@qq.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
web_fetch fetched any http/https URL a model emitted, checking only the
scheme. It performed requests.get(..., allow_redirects=True) with no
hostname resolution check and no private/loopback/link-local/cloud-metadata
filtering, and never re-validated redirect targets. A model (including one
under prompt injection) could make CowAgent fetch 127.0.0.1, RFC1918,
169.254.169.254 or other internal endpoints and return their bodies into
the conversation; a public URL could also 302-bounce into a private target.
The repo already shipped an SSRF validator for the vision tool
(Vision._validate_url_safe). Extract that logic into a shared helper
(agent/tools/utils/url_safety.py) and reuse it:
- execute() now validates the URL before dispatching to either fetch path.
- A new _safe_get() helper disables auto-redirect and follows redirects
manually, re-validating every hop so a public URL cannot bounce into an
internal address. Both the webpage and document fetch paths use it.
- Vision._validate_url_safe now delegates to the shared helper (public API
unchanged), so both URL-consuming tools share one guard.
Stdlib only (ipaddress, socket, urllib.parse); no new dependency. Adds
tests/test_security_ssrf_web_fetch.py covering loopback, cloud-metadata,
RFC1918 and a public->loopback redirect.
Sink: agent/tools/web_fetch/web_fetch.py (_fetch_webpage / _fetch_document).
Signed-off-by: christop <825583681@qq.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