CowAgent is a personal AI assistant you host yourself and reach from wherever you chat. It plans multi-step tasks, uses built-in tools (files, a terminal, a browser, web search, a scheduler) and MCP servers, installs skills from its own Skill Hub or GitHub, and keeps a long-term memory and a Markdown knowledge base that it curates from your conversations. You can also set up a team of agents with different roles and models.
It plugs into almost any model provider, from Claude, GPT, Gemini and DeepSeek to Qwen, Kimi, GLM and a custom endpoint for local models, and into a long list of channels: its own web console, Telegram, Slack, Discord, and the Chinese platforms WeChat, Feishu, DingTalk, WeCom and QQ. A desktop client for macOS and Windows bundles the backend.
The project began in 2022 as chatgpt-on-wechat, which explains the name, and has about 47,600 stars and 193 contributors, at 2.2.1. It is MIT-licensed and written in Python. The docs and defaults lean toward Chinese platforms and the LinkAI service that hosts its installer, though neither is required.
- Repository: github.com/zhayujie/CowAgent
- Licence: MIT (MIT License)
- Language: Python. Stars: 47.6K. Forks: 10.4K. Last push: Oct 25, 2026.
- Scan: safe, Oct 26, 2026, commit d2a72a3
Who it is for
People who want an always-on assistant they can message from their chat apps, teams on Feishu, DingTalk or WeCom in particular, and tinkerers who want an agent with memory running on their own computer or server.
Getting started
1. Linux or macOS: the one-line installer (fetched from the LinkAI CDN)
bash <(curl -fsSL https://cdn.link-ai.tech/code/cow/run.sh)2. Windows (PowerShell)
irm https://cdn.link-ai.tech/code/cow/run.ps1 | iex3. Or with Docker
curl -O https://cdn.link-ai.tech/code/cow/docker-compose.yml && docker compose up -dThen open http://localhost:9899 for the web console. On a server, set web_host to 0.0.0.0 and a web_password in config.json before opening port 9899: the agent can run terminal commands, so an unprotected console is an open door to the machine.
Safety scan
We cloned zhayujie/CowAgent at commit d2a72a3 on Oct 26, 2026 and ran the checks described on the GitHub Tools page: credential patterns, decode-and-execute code, install-time scripts, committed binaries, risky CI workflows, every host the code talks to, known vulnerabilities in pinned dependencies, and project hygiene. A person read every hit. This is what we found.
- The 3 secret hits are the placeholder xoxb-xxx Slack token in the Slack setup docs (English, Japanese and Chinese). The 4 bare-IP URLs are tests of a download guard. No committed binaries.
- run.sh, the 1,354-line installer, uses sudo only to install missing tools with apt or yum, clones the code from GitHub with Gitee as a fallback, and offers to set up model keys. The README fetches it from cdn.link-ai.tech rather than GitHub, so the copy you run is not necessarily the one scanned; read it first if that matters to you.
- The two pattern hits are comments in run.sh about running under curl | bash.
- All 150 advisories (2 critical, in shell-quote and tar) are in desktop/package-lock.json, the Electron desktop client. The Python dependencies are version ranges with no lockfile, so the check had nothing to read there.
- 9 workflows, none using pull_request_target; none of the 10 third-party actions is pinned. Contributing guide present; no security policy, Dependabot or CodeQL.
What the scanner counted
| Check | Result |
|---|---|
| Secrets | 3 candidates found and read; see the notes above. |
| Suspicious code | 2 pattern hits found and read; every one is listed under the raw findings. |
| Install-time code | 3 installer scripts (one can call sudo) |
| Committed binaries | None. |
| CI workflows | 9 workflows. None use pull_request_target. 10 of 10 third-party actions pinned to a tag rather than a commit. |
| Network hosts | 40 distinct hosts referenced from source; most often example.test, cdn.example.com, github.com, open.feishu.cn. 4 URLs to a bare IP address, listed under the raw findings. |
| Known vulnerabilities | 150 advisories across 557 pinned packages: 2 critical, 74 high, 60 moderate, 14 low. desktop/package-lock.json: 627 packages, 150 advisories; requirements-optional.txt: 1 packages, 0 advisories. |
| Project hygiene | Has licence file, contributing guide. Missing security policy, automated dependency updates, CodeQL. |
| OpenSSF Scorecard | Not scored: the project is not in Scorecard's weekly index. |
The raw findings
Every hit the scanner wrote out, with a link to the exact line at the scanned commit. Secrets candidates are redacted.
Secret candidates (3, redacted)
| Where | Rule | Match |
|---|---|---|
| docs/channels/slack.mdx:75 | slack-token | xoxb-x…xxx (17 chars) |
| docs/ja/channels/slack.mdx:75 | slack-token | xoxb-x…xxx (17 chars) |
| docs/zh/channels/slack.mdx:75 | slack-token | xoxb-x…xxx (17 chars) |
Pattern hits (2)
URLs to bare IP addresses (4)
| Where | Rule | Match |
|---|---|---|
| tests/test_media_download_guarded.py:31 | ip-literal-url | replies = {"http://93.184.216.34/hop": _Response(302, "http://169.254.169.254/latest")} |
| tests/test_media_download_guarded.py:55 | ip-literal-url | media_download.download_bytes("http://93.184.216.34/hop", guarded=True) |
| tests/test_media_download_guarded.py:56 | ip-literal-url | assert fetched == ["http://93.184.216.34/hop"] |
| tests/test_media_download_guarded.py:61 | ip-literal-url | assert media_download.download_bytes("http://93.184.216.34/ok", guarded=True) == b"media" |
Installer scripts (3)
- run.sh, 1,354 lines, uses sudo; talks to api.anthropic.com, api.deepseek.com, api.openai.com, generativelanguage.googleapis.com, gitee.com, github.com, link-ai.tech, pypi.org, pypi.tuna.tsinghua.edu.cn
- scripts/run.ps1, 956 lines; talks to api.anthropic.com, api.deepseek.com, api.openai.com, cdn.link-ai.tech, generativelanguage.googleapis.com, git-scm.com, gitee.com, github.com, link-ai.tech, pypi.org, pypi.tuna.tsinghua.edu.cn, www.python.org
- scripts/start.sh, 17 lines
Worst known vulnerabilities (24 of 150)
| Advisory | Severity | Package | Summary |
|---|---|---|---|
| GHSA-w7jw-789q-3m8p | critical | shell-quote@1.8.3 | shell-quote quote() does not escape newlines in object .op values |
| GHSA-23hp-3jrh-7fpw | critical | tar@6.2.1 | node-tar: Decompression/parse DoS via unlimited input |
| GHSA-3jxr-9vmj-r5cp | high | brace-expansion@1.1.12 | brace-expansion: DoS via exponential-time expansion of consecutive non-expanding {} groups |
| GHSA-6j4f-fj2g-mc7p | high | brace-expansion@1.1.12 | brace-expansion: DoS via uncontrolled recursion in parseCommaParts causing stack exhaustion |
| GHSA-mh99-v99m-4gvg | high | brace-expansion@1.1.12 | brace-expansion: DoS via unbounded expansion length causing an out-of-memory process crash |
| GHSA-qhr7-859c-m2p7 | high | brace-expansion@1.1.12 | brace-expansion: DoS via uncontrolled recursion on nested brace groups causing stack exhaustion |
| GHSA-rgw5-rvv9-x895 | high | brace-expansion@1.1.12 | brace-expansion: DoS via unbounded intermediate arrays, bypassing the CVE-2026-14257 mitigation |
| GHSA-3jxr-9vmj-r5cp | high | brace-expansion@2.0.2 | brace-expansion: DoS via exponential-time expansion of consecutive non-expanding {} groups |
| GHSA-6j4f-fj2g-mc7p | high | brace-expansion@2.0.2 | brace-expansion: DoS via uncontrolled recursion in parseCommaParts causing stack exhaustion |
| GHSA-mh99-v99m-4gvg | high | brace-expansion@2.0.2 | brace-expansion: DoS via unbounded expansion length causing an out-of-memory process crash |
| GHSA-qhr7-859c-m2p7 | high | brace-expansion@2.0.2 | brace-expansion: DoS via uncontrolled recursion on nested brace groups causing stack exhaustion |
| GHSA-rgw5-rvv9-x895 | high | brace-expansion@2.0.2 | brace-expansion: DoS via unbounded intermediate arrays, bypassing the CVE-2026-14257 mitigation |
| GHSA-27p8-2357-5qqv | high | @xmldom/xmldom@0.8.11 | xmldom: DocType `name` Injection Bypasses requireWellFormed |
| GHSA-2v35-w6hq-6mfw | high | @xmldom/xmldom@0.8.11 | xmldom: Uncontrolled recursion in XML serialization leads to DoS |
| GHSA-4w3w-2rp5-g8jm | high | @xmldom/xmldom@0.8.11 | xmldom: Attribute name injection via setAttribute() bypasses requireWellFormed |
| GHSA-8344-3jmq-59r6 | high | @xmldom/xmldom@0.8.11 | xmldom: Quadratic-time attribute deduplication |
| GHSA-93r5-fhx6-vmg9 | high | @xmldom/xmldom@0.8.11 | xmldom: Quadratic-time parsing via the malformed-input recovery path - `parseElementStartPart` re-scan and `normalize()`… |
| GHSA-965w-775f-mr7g | high | @xmldom/xmldom@0.8.11 | xmldom: Quadratic-memory consumption |
| GHSA-c7q8-3ch8-vqpv | high | @xmldom/xmldom@0.8.11 | xmldom: Processing Instruction Target Injection Bypasses requireWellFormed |
| GHSA-f6ww-3ggp-fr8h | high | @xmldom/xmldom@0.8.11 | xmldom has XML injection through unvalidated DocumentType serialization |
| GHSA-j759-j44w-7fr8 | high | @xmldom/xmldom@0.8.11 | xmldom has XML node injection through unvalidated comment serialization |
| GHSA-w2rr-34g9-rvrj | high | @xmldom/xmldom@0.8.11 | xmldom: Element name injection via createElement() bypasses requireWellFormed |
| GHSA-wh4c-j3r5-mjhp | high | @xmldom/xmldom@0.8.11 | xmldom: XML injection via unsafe CDATA serialization allows attacker-controlled markup insertion |
| GHSA-x4fp-j954-r2f4 | high | @xmldom/xmldom@0.8.11 | xmldom: End-tag Whitespace-Trim Regex ReDoS - quadratic backtracking in the 0.8.x end-tag parser |
By the numbers
| Stars | 47.6K |
|---|---|
| Forks | 10.4K |
| Contributors | 193 |
| Commits | 4,289 |
| Open issues | 15 |
| Open pull requests | 30 |
| Releases | 85 |
| Latest release | 2.2.1 |
| Licence | MIT |
| Main language | Python |
| Project age | 4 years |
| Last push | Oct 25, 2026 |
| Tracked files | 1,618 |
| Lines of code | 304.5K |
| Checkout size | 15 MB |
Lines by language: Python 194.1K, TypeScript 36.1K, Markdown 30.9K, JavaScript 27.1K, CSS 7,253, HTML 3,004.
Questions
Is CowAgent free?
Yes. CowAgent is free and open source under the MIT licence. You pay for the model provider you connect, unless you point it at a local model; LinkAI, which offers one key for many models, is an optional paid service.
Which chat apps does CowAgent work with?
Its own web console, Telegram, Slack, Discord, WeChat, Feishu (Lark), DingTalk, WeCom, QQ, WeChat customer service and WeChat Official Accounts. One instance can serve several channels at once, and most can be connected from the web console.
Can CowAgent use a local model?
Yes, through its custom provider setting, which accepts a local model server or a third-party proxy with an OpenAI-compatible API. Its tool use and planning work best with a strong model, so a small local one will limit what the agent can do.
This post is part of GitHub Tools, where every repository is cloned and scanned before it is written up. The scan is a snapshot of one commit on one day; the repository has moved on since, so check it before you install.
