From 5650983cf73f7539ca3c4b31bef35c01ed5a4a17 Mon Sep 17 00:00:00 2001
From: Costa Tin <12409467+C0staTin@users.noreply.github.com>
Date: Fri, 26 Jun 2026 16:34:34 -0600
Subject: [PATCH 1/2] =?UTF-8?q?Refresh=20the=20Anton=20README=20=E2=80=94?=
=?UTF-8?q?=20position=20as=20the=20agent=20powering=20Cowork?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Updates the README to reflect that Anton is the open-source agentic harness behind MindsHub Cowork, swaps in the Cowork app downloads, and cleans up stale references.
### Changes
- **Reposition**: Anton is "the open-source agentic harness that powers MindsHub Cowork" — the default agent inside Cowork, and standalone via the CLI. Dropped the "alternative to Claude-Cowork" framing (collides with the *Cowork* product name).
- **Downloads → Cowork**: removed the standalone Anton `.pkg`/`.exe` links; the desktop/web path now points to `console.mindshub.ai` (web) and the `mindshub-cowork` macOS/Windows builds. Kept the Anton CLI install (curl/PowerShell).
- **Community**: added Discord (`mindshub.ai/discord`) in the badges/nav and a new **Help & community** section; added **Contributing** (PRs target `dev`) and a **Security** pointer.
- **License**: footer previously claimed **AGPL-3.0**, but the repo's `LICENSE` file is **MIT**. Removed the hardcoded mention to avoid future drift — the README now relies on GitHub's license display + a dynamic badge. ⚠️ If Anton is meant to be AGPL, the `LICENSE` file needs updating (separate change).
- **Cleanups**: fixed typos (`Calendarss`, "was was", "connect an interact", "unsubscribable"); "at MindsDB" → "at MindsHub" in the naming section.
- **UTMs** on `mindshub.ai` links (`utm_campaign=anton-readme`); downloads and Discord left bare.
- **Preserved** all technical/maintainer content (web search table, workspace layout, firewall, analytics, trace headers, Dev guidelines, Versioning, Releasing) with heading anchors intact, plus the promo image, gifs, and DeepWiki badge.
---
README.md | 126 ++++++++++++++++++++++++++++++------------------------
1 file changed, 71 insertions(+), 55 deletions(-)
diff --git a/README.md b/README.md
index 04ec7935..309e60ff 100644
--- a/README.md
+++ b/README.md
@@ -1,39 +1,51 @@
+
+
+# Anton - a self-improving agent
+[](https://github.com/mindsdb/anton/stargazers)
+[](LICENSE)
+[](https://mindshub.ai/discord)
+[](https://deepwiki.com/mindsdb/anton)
-# Meet Anton
+[MindsHub Cowork](https://mindshub.ai/?utm_source=github&utm_medium=repo-readme&utm_campaign=anton-readme) ·
+[Docs](https://docs.mindshub.ai/?utm_source=github&utm_medium=repo-readme&utm_campaign=anton-readme) ·
+[Discord](https://mindshub.ai/discord)

-[](https://deepwiki.com/mindsdb/anton)
+---
+
-Anton is a self-improving AI agent you can hand off any task to; Create and send reports, clear your inbox, send emails, manage your calendar, CRM, book flights, etc. An open, powerful alternative to Claude-Cowork that you can run anywhere and use with any model you want — OpenAI, Anthropic, OpenRouter (200+ models), NVIDIA Nemotron, z.ai/GLM, Kimi/Moonshot, MiniMax, or your own endpoint.
+Anton is the open-source agentic harness that powers **[MindsHub Cowork](https://mindshub.ai/?utm_source=github&utm_medium=repo-readme&utm_campaign=anton-readme)** — a self-improving AI agent you can hand any task to. Built and maintained by the MindsHub team, Anton runs as the **default agent** inside Cowork, and just as well **standalone** in your terminal — anywhere, with any model.
+Hand it real work: create and send reports, clear your inbox, manage your calendar and CRM, book flights, and more. You describe the outcome — Anton figures out the steps.
-## Quick Install
-Anton can be installed as a desktop application or as a command-line tool.
+## Get started
-### Desktop App:
+Anton is the default agent in **MindsHub Cowork**, the unified workspace with data connectors, a model router, and artifacts. Get the full experience, or run Anton on its own from the terminal.
-- **macOS**: Click [here to download](https://downloads.mindsdb.com/anton/mac/anton-latest.pkg) the Anton Desktop App for MacOS.
+### MindsHub Cowork app
-- **Windows**: Click [here to download](https://downloads.mindsdb.com/anton/windows/anton-latest.exe) the Anton Desktop App for Windows.
-
-### or - Command-Line App:
+- **Web** — nothing to install: open **[console.mindshub.ai](https://console.mindshub.ai/?utm_source=github&utm_medium=repo-readme&utm_campaign=anton-readme)**.
+- **macOS** — [download the desktop app](https://downloads.mindsdb.com/mindshub-cowork/mac/mindshub-cowork-latest.pkg) (`.pkg`).
+- **Windows** — [download the desktop app](https://downloads.mindsdb.com/mindshub-cowork/windows/mindshub-cowork-latest.exe) (`.exe`).
+
+### Anton CLI (standalone)
-Open your terminal and use the following command to install
+Run Anton directly in your terminal:
-- **macOS/Linux**:
+- **macOS/Linux**
```bash
-curl -sSf https://raw.githubusercontent.com/mindsdb/anton/main/install.sh | sh && export PATH="$HOME/.local/bin:$PATH"
+curl -sSf https://raw.githubusercontent.com/mindsdb/anton/main/install.sh | sh && export PATH="$HOME/.local/bin:$PATH"
```
-- **Windows** (PowerShell):
+- **Windows** (PowerShell)
```powershell
irm https://raw.githubusercontent.com/mindsdb/anton/main/install.ps1 | iex
```
-That's it, you can now run it by simply typing the command.
+That's it — now run it by typing:
```
anton
@@ -43,53 +55,48 @@ anton
### 🔧 Ask for anything that requires action
-- **Send emails** - connect accounts, draft messages or even send them on your behalf.
-- **Manage Calendarss** - Summarize your day, create meetings, block time, etc. All just by asking.
-- **Automated reporting** - pull from multiple databases, crunch numbers, deliver a report on a schedule.
-- **Workflow automation** - monitor a source, react to changes, take action.
-- **Research & synthesis** - scrape the web, summarize findings, build a reference document.
-- **Data pipeline prototyping** - connect sources, transform data, load into a destination.
-- **System administration** - audit configurations, generate reports, fix issues.
+- **Send emails** — connect accounts, draft messages, or even send them on your behalf.
+- **Manage calendars** — summarize your day, create meetings, block time, and more, all just by asking.
+- **Automated reporting** — pull from multiple databases, crunch numbers, deliver a report on a schedule.
+- **Workflow automation** — monitor a source, react to changes, take action.
+- **Research & synthesis** — scrape the web, summarize findings, build a reference document.
+- **Data pipeline prototyping** — connect sources, transform data, load into a destination.
+- **System administration** — audit configurations, generate reports, fix issues.
The pattern is always the same: you describe the outcome, Anton figures out the steps. From one-off tasks to scheduled workflows — Anton handles it. Here are a few examples:
-### 📊 Data analysis & Reports
+### 📊 Data analysis & reports
```
I hold 50 AAPL, 200 NVDA, and 10 AMZN. Get today's prices, calculate my
total portfolio value, show me the 30-day performance of each stock, and
any other information that might be useful. Give me a complete dashboard.
```
-What happens next is the interesting part. At first, Anton doesn't have any particular skill related to this question. However, it figures it out live: scrapes live prices, writes code on the fly, crunches the numbers, and builds you a full dashboard - all in one conversation, with no setup.
-
+What happens next is the interesting part. At first, Anton doesn't have any particular skill related to this question. However, it figures it out live: scrapes live prices, writes code on the fly, crunches the numbers, and builds you a full dashboard — all in one conversation, with no setup.

-
### 📬 Email cleanup
```
Dear Anton, please help me clear unwanted emails...
```
-Anton scans your inbox, classifies emails by signal vs. noise, identifies unsubscribable marketing, cold outreach, and internal tool notifications - then surfaces a breakdown and handles the cleanup. One user ran it on ~1,000 emails and found ~35% were un-subscribable. Anton surfaced everything AND handled the cleanup.
+Anton scans your inbox, classifies emails by signal vs. noise, identifies unsubscribable marketing, cold outreach, and internal tool notifications — then surfaces a breakdown and handles the cleanup. One user ran it on ~1,000 emails and found ~35% were unsubscribable. Anton surfaced everything AND handled the cleanup.
### 💬 Build its own integrations
```
Set up a WhatsApp integration so I can message you from my phone.
```
-Anton doesn't wait for someone to build a connector. It writes the integration code itself, sets it up, and gets it running - so you can chat with it from WhatsApp, Telegram, or whatever channel you need.
-
-
-
+Anton doesn't wait for someone to build a connector. It writes the integration code itself, sets it up, and gets it running — so you can chat with it from WhatsApp, Telegram, or whatever channel you need.
---
## Key features
-- **Credential vault** - prevents secrets from being exposed to LLMs.
-- **Isolated code execution** - protected, reproducible "show your work" environment.
-- **Multi-layer memory & continuous learning** - session, semantic and long-term knowledge. Anton remembers what it learned and gets better at your specific workflows over time.
-- **Web search & fetch** - the agent can query the live web and retrieve URL contents. Routed natively through your LLM provider when possible (no extra setup), with a transparent fallback for third-party endpoints. See below.
+- **Credential vault** — prevents secrets from being exposed to LLMs.
+- **Isolated code execution** — protected, reproducible "show your work" environment.
+- **Multi-layer memory & continuous learning** — session, semantic, and long-term knowledge. Anton remembers what it learned and gets better at your specific workflows over time.
+- **Web search & fetch** — the agent can query the live web and retrieve URL contents. Routed natively through your LLM provider when possible (no extra setup), with a transparent fallback for third-party endpoints. See below.
---
@@ -101,7 +108,7 @@ Anton exposes two web tools to the agent — `web_search` and `web_fetch` — bo
| --- | --- | --- | --- |
| Anthropic BYOK | Anthropic native server tool | Anthropic native server tool | None — billed on your Anthropic key |
| OpenAI BYOK | OpenAI Responses API native | covered by `web_search` | None — billed on your OpenAI key |
-| Minds-Enterprise-Cloud (mdb.ai) | mdb.ai passthrough | mdb.ai passthrough | None — billed on your Minds key |
+| MindsHub Model Router | passthrough | passthrough | None — billed on your MindsHub API key |
| Generic OpenAI-compatible (Together, Groq, Ollama, vLLM, …) | Exa.ai or Brave (you choose at setup) | stdlib HTTP GET (no key) | Run `anton setup-search` once |
For the first three rows there's nothing to configure — the LLM provider executes the tools server-side and the results are folded directly into its response. For the fourth row, after `anton setup` finishes configuring a custom OpenAI-compatible endpoint Anton will offer to set up Exa or Brave; you can also (re)run that step at any time with `anton setup-search`. The chosen search-provider key is persisted to `~/.anton/.env` so it carries across sessions and workspaces, exactly like your LLM key.
@@ -113,7 +120,7 @@ Caveats: provider rate limits apply; `web_fetch` has a 30-second timeout and str
---
#### Connect your data and apps
-Anton can connect an interact with files, databases, applications, APIs,... etc..
+Anton can connect to and interact with files, databases, applications, APIs, and more.
```powershell
/connect
@@ -125,7 +132,7 @@ Anton can connect an interact with files, databases, applications, APIs,... etc.
Tell Anton to connect and ask questions about your data. It will find credentials in the vault, fetch the schema, and retrieve what it needs.
```terminal
-YOU> Connect to my Gmail and find emails from potential customers that haven’t been handled.
+YOU> Connect to my Gmail and find emails from potential customers that haven't been handled.
ANTON>
⎿ Connecting and fetching emails...
@@ -136,22 +143,22 @@ ANTON>
## What's inside
-A big part of what makes Anton work is that it doesn’t need a huge collection of separate tools for web, DB, files etc. Most of the work is done through one core harness: The execution scratchpad, which can dynamically become whatever Anton needs for the task.
+A big part of what makes Anton work is that it doesn't need a huge collection of separate tools for web, DB, files, etc. Most of the work is done through one core harness: the execution scratchpad, which can dynamically become whatever Anton needs for the task.
-For the full architecture of Anton, and developer guide, see **[anton/README.md](anton/README.md)**.
+For the full architecture of Anton, and the developer guide, see **[anton/README.md](anton/README.md)**.
---
## Workspace layout
When you run `anton` in a directory:
-- `.anton/` - workspace folder containing scratchpad state, episodic memory, and local secrets.
-- `.anton/anton.md` - optional project context (Anton reads this at conversation start).
-- `.anton/.env` - workspace configuration variables file (local file).
-- `.anton/episodes/*` - episodic memories, one file per session.
-- `.anton/memory/rules.md` - behavioral rules: Always/never/when rules (e.g., never hardcode credentials, how to build HTML)
-- `.anton/memory/lessons.md` - factual knowledge: Things I've learned (stock API quirks, dashboard patterns, data fetching notes)
-- `.anton/memory/topics/*` - topic-specific lessons: Deeper notes organized by subject (dashboard-visualization, stock-data-api, etc.)
+- `.anton/` — workspace folder containing scratchpad state, episodic memory, and local secrets.
+- `.anton/anton.md` — optional project context (Anton reads this at conversation start).
+- `.anton/.env` — workspace configuration variables file (local file).
+- `.anton/episodes/*` — episodic memories, one file per session.
+- `.anton/memory/rules.md` — behavioral rules: always/never/when rules (e.g., never hardcode credentials, how to build HTML).
+- `.anton/memory/lessons.md` — factual knowledge: things I've learned (stock API quirks, dashboard patterns, data fetching notes).
+- `.anton/memory/topics/*` — topic-specific lessons: deeper notes organized by subject (dashboard-visualization, stock-data-api, etc.).
Override the working folder:
```bash
@@ -170,15 +177,29 @@ netsh advfirewall firewall add rule name="Anton Scratchpad" dir=out action=allow
---
## How Anton differs from coding agents
-Anton is a *doing* agent: code is a means, not the end. Where coding agents focus on producing code for a codebase, Anton focuses on delivering the outcome - a cleaned inbox, a live dashboard, a working integration, an automated workflow - and will write whatever code is necessary to achieve that goal.
+Anton is a *doing* agent: code is a means, not the end. Where coding agents focus on producing code for a codebase, Anton focuses on delivering the outcome — a cleaned inbox, a live dashboard, a working integration, an automated workflow — and will write whatever code is necessary to achieve that goal.
+
+---
+
+## 💬 Help & community
+
+- **Chat with us** — join the [Discord community](https://mindshub.ai/discord).
+- **Report a bug or request a feature** — open a [GitHub issue](https://github.com/mindsdb/anton/issues) with reproduction steps.
+- **Read the docs** — guides and setup at [docs.mindshub.ai](https://docs.mindshub.ai/?utm_source=github&utm_medium=repo-readme&utm_campaign=anton-readme).
+
+## 🤝 Contributing
+
+Anton is open source (MIT) and contributions are welcome — new capabilities, integrations, docs, and bug reports. Non-hotfix PRs target the `dev` branch (see [Dev guidelines](#dev-guidelines)). Say hi in the **#contributors** space on [Discord](https://mindshub.ai/discord).
+
+**Security:** Found a vulnerability? Please don't open a public issue — report it privately via our [security policy](https://github.com/mindsdb/anton/security).
---
## Is "Anton" a Mind?
-Yes, at MindsDB we build AI systems that collaborate with people to accomplish tasks, inspired by the culture series books, so yes, Anton is a Mind :)
+Yes. At MindsHub we build AI systems that collaborate with people to accomplish tasks, inspired by the *Culture* series of books — so yes, Anton is a Mind :)
## Why the name "Anton"?
-We really enjoyed the show *Silicon Valley*. Gilfoyle's AI - Son of Anton - was an autonomous system that wrote code, made its own decisions, and occasionally went rogue. We thought it was was great name for an AI that can learn on its own, so we kept Anton, dropped the "Son of".
+We really enjoyed the show *Silicon Valley*. Gilfoyle's AI — Son of Anton — was an autonomous system that wrote code, made its own decisions, and occasionally went rogue. We thought it was a great name for an AI that can learn on its own, so we kept Anton and dropped the "Son of".
---
@@ -309,8 +330,3 @@ Anything under [`.github/`](.github/) is owned by `@mindsdb/devops` via [CODEOWN
### Hotfixes / out-of-band releases
If you genuinely need to release outside the normal flow (e.g. an admin hotfix), coordinate with `@mindsdb/devops` to bypass the tag ruleset. The e2e workflow's version-match guard will still verify the release tag matches `anton.__version__` and fail loudly on mismatch.
-
----
-
-## License
-AGPL-3.0 license
From 074c931107b27406658dd573adc2ca1d0ec82969 Mon Sep 17 00:00:00 2001
From: Jorge Torres
Date: Fri, 3 Jul 2026 08:44:05 -0700
Subject: [PATCH 2/2] fix(llm): defer tool_result images to after all role:tool
responses (ENG-569)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
When a multi-tool assistant turn has a non-final tool_result with image
content, _translate_user_blocks was emitting a role:user image message
inline — immediately after each role:tool response. This wedged a user
message between tool responses, leaving subsequent tool_use ids without
a matching tool_result block → Anthropic 400 via the minds router.
Fix: accumulate extracted images in deferred_img_parts across the loop
and flush them as a single role:user message after all role:tool messages.
Adds a regression test covering the multi-tool + image-result case.
Co-Authored-By: Claude Sonnet 4.6
---
anton/core/llm/openai.py | 30 ++++++++++------
tests/test_openai_provider.py | 66 +++++++++++++++++++++++++++++++++++
2 files changed, 86 insertions(+), 10 deletions(-)
diff --git a/anton/core/llm/openai.py b/anton/core/llm/openai.py
index c2f0549c..87b6ef8c 100644
--- a/anton/core/llm/openai.py
+++ b/anton/core/llm/openai.py
@@ -135,6 +135,14 @@ def _translate_user_blocks(blocks: list[dict], supports_vision: bool = True, vis
"""
result: list[dict] = []
content_parts: list[dict] = [] # Accumulates text + image blocks
+ # Images extracted from tool_results are deferred here and flushed as a
+ # single role:user message AFTER all role:tool messages. Emitting them
+ # inline (immediately after each role:tool) breaks the multi-tool case:
+ # when an assistant turn contains N tool_use blocks and a non-final one
+ # returns images, the inline role:user message lands between role:tool
+ # responses, leaving the remaining tool_use ids without a corresponding
+ # tool_result immediately after → Anthropic 400.
+ deferred_img_parts: list[dict] = []
for block in blocks:
if block.get("type") == "tool_result":
@@ -145,7 +153,8 @@ def _translate_user_blocks(blocks: list[dict], supports_vision: bool = True, vis
# tool_result -> role:tool message. The Chat Completions API only
# accepts a string in `tool` messages, so when the tool returned
# multimodal blocks (image + text), we split them: text → tool
- # message, image → a follow-up role:user message right after.
+ # message, images → a deferred role:user message emitted after
+ # ALL role:tool messages to keep tool responses contiguous.
raw = block.get("content", "")
extra_images: list[dict] = []
if isinstance(raw, list):
@@ -173,21 +182,19 @@ def _translate_user_blocks(blocks: list[dict], supports_vision: bool = True, vis
)
if extra_images:
- img_parts: list[dict] = [
- {
- "type": "text",
- "text": "Image(s) returned by previous tool call:",
- }
- ]
+ if not deferred_img_parts:
+ deferred_img_parts.append(
+ {"type": "text", "text": "Image(s) returned by previous tool call:"}
+ )
for img in extra_images:
if vision_format == "anthropic":
- img_parts.append(img)
+ deferred_img_parts.append(img)
continue
source = img.get("source", {})
if source.get("type") == "base64":
media_type = source.get("media_type", "image/png")
data = source.get("data", "")
- img_parts.append(
+ deferred_img_parts.append(
{
"type": "image_url",
"image_url": {
@@ -195,7 +202,6 @@ def _translate_user_blocks(blocks: list[dict], supports_vision: bool = True, vis
},
}
)
- result.append({"role": "user", "content": img_parts})
elif block.get("type") == "text":
content_parts.append({"type": "text", "text": block.get("text", "")})
elif block.get("type") == "image" and supports_vision:
@@ -241,6 +247,10 @@ def _translate_user_blocks(blocks: list[dict], supports_vision: bool = True, vis
# Already OpenAI format — pass through.
content_parts.append(block)
+ # Flush deferred images after all role:tool messages.
+ if deferred_img_parts:
+ result.append({"role": "user", "content": deferred_img_parts})
+
if content_parts:
# If only text parts, flatten to a simple string for compatibility
if all(p.get("type") == "text" for p in content_parts):
diff --git a/tests/test_openai_provider.py b/tests/test_openai_provider.py
index 95014553..7b2c8737 100644
--- a/tests/test_openai_provider.py
+++ b/tests/test_openai_provider.py
@@ -220,6 +220,72 @@ def test_translate_messages_with_tool_result(self):
assert tool_msg["tool_call_id"] == "tool_1"
assert tool_msg["content"] == "file contents here"
+ def test_multi_tool_with_image_result_keeps_tools_contiguous(self):
+ """Regression: when a non-final tool_result carries image content, the
+ extracted role:user image message must NOT be inserted between role:tool
+ messages — that breaks the Anthropic contract (every tool_use must have
+ a tool_result immediately after the assistant turn) and the OpenAI
+ contract (no role:user between tool responses).
+
+ Expected layout after translation:
+ system → assistant (2 tool_calls) → tool(toolu_RENDER) →
+ tool(toolu_FINDPATH) → user(images)
+ """
+ img_block = {
+ "type": "image",
+ "source": {"type": "base64", "media_type": "image/png", "data": "abc123"},
+ }
+ msgs = [
+ {
+ "role": "assistant",
+ "content": [
+ {"type": "tool_use", "id": "toolu_RENDER", "name": "render_pdf", "input": {}},
+ {"type": "tool_use", "id": "toolu_FINDPATH", "name": "find_path", "input": {}},
+ ],
+ },
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "tool_result",
+ "tool_use_id": "toolu_RENDER",
+ "content": [
+ {"type": "text", "text": "Rendered page 1."},
+ img_block,
+ ],
+ },
+ {
+ "type": "tool_result",
+ "tool_use_id": "toolu_FINDPATH",
+ "content": "path/to/file.pdf",
+ },
+ ],
+ },
+ ]
+ result = _translate_messages("sys", msgs, supports_vision=True, vision_format="anthropic")
+
+ roles = [m["role"] for m in result]
+ # system, assistant, tool(RENDER), tool(FINDPATH), user(images)
+ assert roles == ["system", "assistant", "tool", "tool", "user"], (
+ f"Expected tools contiguous before image user-msg, got: {roles}"
+ )
+
+ # Both tool messages must appear before any user message
+ tool_indices = [i for i, m in enumerate(result) if m["role"] == "tool"]
+ user_indices = [i for i, m in enumerate(result) if m["role"] == "user"]
+ assert all(t < u for t in tool_indices for u in user_indices), (
+ "role:user image message must come after all role:tool messages"
+ )
+
+ # The tool responses map to the right call ids
+ assert result[2]["tool_call_id"] == "toolu_RENDER"
+ assert result[3]["tool_call_id"] == "toolu_FINDPATH"
+
+ # The trailing user message carries the image
+ img_user = result[4]
+ assert isinstance(img_user["content"], list)
+ assert any(p.get("type") == "image" for p in img_user["content"])
+
class TestFromSettingsOpenAI:
def test_from_settings_openai(self):