A coding-agent CLI and strongly-typed Python library -- self-mutating, hot-swapping, multi-provider, with async tool calls and bidirectional recursive spawn.
38
stars
527
commits
Python
primary language
Sep 15, 2026
updated
A coding-agent CLI and strongly-typed Python library -- self-mutating, hot-swapping, multi-provider, with async tool calls and bidirectional recursive spawn.
Tutorial · Concepts · Providers · Tools · CLI · Sessions · Security · Architecture · API · Streaming · Compaction · Slack · Self-hosted · Showcase · Examples
# Mac:
# # Required for quick install.
# brew install uv
# # Optional for improved performance.
# brew install ripgrep fd
# Ubuntu/Debian:
# # Required for quick install.
# sudo apt-get install -y curl
# curl -LsSf https://astral.sh/uv/install.sh | sh
# # Optional for improved performance.
# sudo apt-get install -y ripgrep fd-find
uv tool install sagent
sagent
Things Claude Code, Codex CLI, and Gemini CLI don't do:
llama.cpp server, all behind one binary.--max-budget-usd N caps the whole tree.AgentSend: "switch to o1, crank thinking, recompact and drop the file reads."AgentSend to any peer, so coordination is a tree, not a star. Claude Code's experimental Agent Teams is flat (one lead, no nesting); Codex and Gemini have no peer messaging.PaperSearch/PaperFetch walk citation graphs and fetch PDFs, multi-backend WebSearch, WebFetch with markdown extraction, atomic read/write tracking on file tools.stdin, stdout, exit codes, and --output-format json are first-class. Pipe through jq, drop into ipython (same prompt_toolkit underneath).Agent class powers the CLI, your application code, and recursive sub-agents.Agent, Tool, Model, Provider, and Message are protocols and dataclasses you import, compose, and unit-test.AgentSend to any other named peer -- not just its parent. Like user input, peer messages preempt the receiving agent's tool calls, so no agent blocks waiting on a stuck child.Use it as a library:
from sagent import tools
from sagent.agent import Agent
from sagent.lib.custom_json import json_freeze
from sagent.providers import Google
agent = Agent(
model=Google.from_env().model("gemini-3.1-pro-preview"),
system="You are a scientist.",
tools=[tools.Read(), tools.Glob(), tools.Grep()],
)
result = await agent.run(json_freeze({"prompt": "analyze the CSV in ./data/"}))
print(result.content)
Sagent requires Python 3.12 or newer. ripgrep and fd-find are
optional -- sagent has Python fallbacks when absent -- but recommended
for faster Grep / Glob. PDF rendering uses the bundled pypdfium2
wheel and needs no system install. The Quick Start
above installs the sagent CLI.
Add sagent to your own project as a library:
uv add sagent
Or run from a source checkout:
git clone --depth 1 https://github.com/rekursiv-ai/sagent.git
cd sagent
uv run sagent --help
Bare sagent uses Anthropic and reads ANTHROPIC_API_KEY:
export ANTHROPIC_API_KEY=...
sagent
Pick a different provider by setting its key (see
Provider setup) and passing --provider:
export OPENAI_API_KEY=...
sagent --provider OpenAI
--provider defaults to the first name in --allow-providers, so
SAGENT_ALLOW_PROVIDERS alone picks the default backend and also caps
which providers spawned sub-agents may use:
SAGENT_ALLOW_PROVIDERS=OpenAI sagent # OpenAI is now the default provider
Pipe a prompt on stdin for non-interactive use:
printf 'Say hi in one sentence.' | \
sagent --provider OpenAI --output-format json
Use --continue to resume the most recent session for this working directory, --session PATH for an explicit session directory, or --ephemeral when prompts and auto-memory should not be written to disk. Use --max-budget-usd N to cap API spend for the current run.
See CLI and Sessions for the full flag set.
import asyncio
from sagent import tools
from sagent.agent import Agent
from sagent.lib.custom_json import json_freeze
from sagent.providers import Anthropic
async def main() -> None:
agent = Agent(
model=Anthropic.from_env().model("claude-sonnet-4-6"),
system="You are a concise coding assistant.",
tools=[tools.Read(), tools.Grep(), tools.Glob()],
)
result = await agent.run(json_freeze({"prompt": "Summarize README.md"}))
print(result.content)
asyncio.run(main())
Agent.run() accepts a JSON directive with a prompt key and returns a Message.
See API, Tutorial, and Concepts for more detail.
Sagent ships API-key providers for Anthropic, OpenAI, OpenAISubscription, Google, Moonshot, DashScope, MiniMax, and generic OpenAI-compatible endpoints, a subscription-backed AnthropicCLI that rides your installed claude login, plus a managed local LlamaCpp provider. Set the key (or run the login) for the provider you plan to use:
export ANTHROPIC_API_KEY=...
export OPENAI_API_KEY=...
export GOOGLE_API_KEY=...
export MOONSHOT_API_KEY=...
export DASHSCOPE_API_KEY=...
export MINIMAX_API_KEY=...
and
export SAGENT_ALLOW_PROVIDERS=...
to set the default value of the --provider flag.
| Provider | Environment variable | Example model |
|---|---|---|
Anthropic | ANTHROPIC_API_KEY | claude-sonnet-4-6 |
AnthropicCLI | none (claude auth login --claudeai) | claude-sonnet-4-6 |
OpenAI | OPENAI_API_KEY | gpt-5.6-sol |
Google | GOOGLE_API_KEY | gemini-3.1-pro-preview |
Moonshot | MOONSHOT_API_KEY | kimi-k2.6 |
DashScope | DASHSCOPE_API_KEY | qwen3.6-plus |
MiniMax | MINIMAX_API_KEY | MiniMax-M2.7 |
SelfHosted | none | Qwen/Qwen3.6-27B |
LlamaCpp | none (uses LLAMA_CPP_MODEL + LLAMA_CPP_SERVER) | qwen3.6-27b-12gb |
See Providers for the provider matrix, inference rules, and OpenAI-compatible provider setup.
Install the local runtime extra from a checkout:
uv sync --extra selfhosted
Or add it to your project from PyPI:
uv add "sagent[selfhosted]"
Then pass a HuggingFace repo ID or local snapshot path:
sagent --provider SelfHosted --model Qwen/Qwen3.6-27B+bfloat16+cuda
sagent --provider SelfHosted --model Qwen/Qwen3.6-27B+cuda+bfloat16
For a small smoke test:
sagent --provider SelfHosted --model Qwen/Qwen3-0.6B+float16+cuda \
--effort none --max-response-tokens 32 --max-tool-call-rounds 1
SelfHosted options use + suffixes after the model name. Device, dtype, and compile can appear in any order, but each category can appear once.
The LlamaCpp provider is a second local option: it manages a
llama-server subprocess and talks to it over its OpenAI-compatible
endpoint. Point LLAMA_CPP_SERVER at a built llama-server binary and
LLAMA_CPP_MODEL at a .gguf file, then run
sagent --provider LlamaCpp --model qwen3.6-27b-12gb.
See Self-hosted Models for options, local snapshot paths, and runtime requirements.
The examples/ directory contains small, runnable examples:
offline_custom_tool.py: run an agent/tool/model loop without API keys.decorator_tool.py: wrap a function as a tool.custom_tool.py: implement the full Tool protocol.multi_agent_reviewer.py: spawn an isolated reviewer child.openai_compatible_provider.py: connect an OpenAI-compatible endpoint.Start with the tutorial, then use the examples as copyable patterns. See Examples and Tools.
Sagent is an agent runtime, not a sandbox. Enabled tools run with the current
process permissions: Bash executes local commands, file tools read and write
accessible paths, and provider/network tools send data to their configured
services. Sessions are plaintext local state and may contain prompts, model
responses, tool results, file snippets, and paths.
Use narrow tool sets, pass --ephemeral for one-off sensitive
prompts so sessions and auto-memory are disabled, and run Sagent inside your own
OS/container sandbox when a task needs hard isolation. See
Security.
Not yet in Sagent: MCP, LSP, native sandboxing, desktop UI, tree-sitter repo map, hosted service, browser automation.
This comparison focuses on the runtime shape rather than every feature of each project.
| Sagent | aider | LangChain | OpenClaw | Cline | Claude Code | Codex CLI | Gemini CLI | Flue | Pi | Attractor | npcsh | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Python library | ✅ | 🟡 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Multi-provider | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ |
| Context compaction | ✅ | 🟡 | 🟡 | ❌ | 🟡 | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ |
| User-initiated backend swap | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ |
| Agent-initiated backend swap | ✅ | ❌ | 🟡 | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | 🟡 | ❌ |
| Agent self-mutation | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | 🟡 | ❌ | 🟡 |
| Context hot-swap | ✅ | 🟡 | 🟡 | 🟡 | 🟡 | ❌ | ❌ | ❌ | 🟡 | ✅ | ✅ | ❌ |
| Recursive agent spawn | ✅ | ❌ | ✅ | 🟡 | ❌ | 🟡 | 🟡 | ❌ | ✅ | 🟡 | ✅ | ✅ |
| Multi-agent (fully detached) | ✅ | ❌ | ✅ | ✅ | ❌ | 🟡 | 🟡 | ❌ | ✅ | 🟡 | ✅ | 🟡 |
| GitHub stars (May 2026) | -- | 44.4k | 135.8k | 368.6k | 61.4k | -- | 80.1k | 103.2k | 2.5k | 48.6k | 1.1k | 388 |
✅ = yes, 🟡 = partial, ❌ = no. Corrections welcome -- open a PR.
/model swap, tree-sitter repo map, no multi-agent.session.task() delegation, model chosen per call (no agent-initiated swap), no UI/compaction./reload soft self-mutation, sub-agents opt-in only.sagent (noun, neologism) SAY-jent /ˈseɪ.dʒənt/
From sage + agent.
An AI assistant that confidently performs a task you didn't ask for while ignoring the one you did.
"I asked the sagent to fix one failing test -- it deleted the test and reported all green."
See CONTRIBUTING.md for local validation and public contribution flow.
Sibling projects in the rekursiv-ai family:
mdcat CLI.If you find our work useful, please consider citing:
@misc{rekursivai2026sagent,
title={Sagent - A coding-agent CLI and strongly-typed Python library -- self-mutating, hot-swapping, multi-provider, with async tool calls and bidirectional recursive spawn.},
author={Joshua V. Dillon and Dan Kondratyuk},
year={2026},
howpublished={Github},
url={https://github.com/rekursiv-ai/sagent},
}
Python
100.0%
A coding-agent CLI and strongly-typed Python library -- self-mutating, hot-swapping, multi-provider, with async tool calls and bidirectional recursive spawn.
38
stars
527
commits
Python
primary language
Sep 15, 2026
updated
A coding-agent CLI and strongly-typed Python library -- self-mutating, hot-swapping, multi-provider, with async tool calls and bidirectional recursive spawn.
Tutorial · Concepts · Providers · Tools · CLI · Sessions · Security · Architecture · API · Streaming · Compaction · Slack · Self-hosted · Showcase · Examples
# Mac:
# # Required for quick install.
# brew install uv
# # Optional for improved performance.
# brew install ripgrep fd
# Ubuntu/Debian:
# # Required for quick install.
# sudo apt-get install -y curl
# curl -LsSf https://astral.sh/uv/install.sh | sh
# # Optional for improved performance.
# sudo apt-get install -y ripgrep fd-find
uv tool install sagent
sagent
Things Claude Code, Codex CLI, and Gemini CLI don't do:
llama.cpp server, all behind one binary.--max-budget-usd N caps the whole tree.AgentSend: "switch to o1, crank thinking, recompact and drop the file reads."AgentSend to any peer, so coordination is a tree, not a star. Claude Code's experimental Agent Teams is flat (one lead, no nesting); Codex and Gemini have no peer messaging.PaperSearch/PaperFetch walk citation graphs and fetch PDFs, multi-backend WebSearch, WebFetch with markdown extraction, atomic read/write tracking on file tools.stdin, stdout, exit codes, and --output-format json are first-class. Pipe through jq, drop into ipython (same prompt_toolkit underneath).Agent class powers the CLI, your application code, and recursive sub-agents.Agent, Tool, Model, Provider, and Message are protocols and dataclasses you import, compose, and unit-test.AgentSend to any other named peer -- not just its parent. Like user input, peer messages preempt the receiving agent's tool calls, so no agent blocks waiting on a stuck child.Use it as a library:
from sagent import tools
from sagent.agent import Agent
from sagent.lib.custom_json import json_freeze
from sagent.providers import Google
agent = Agent(
model=Google.from_env().model("gemini-3.1-pro-preview"),
system="You are a scientist.",
tools=[tools.Read(), tools.Glob(), tools.Grep()],
)
result = await agent.run(json_freeze({"prompt": "analyze the CSV in ./data/"}))
print(result.content)
Sagent requires Python 3.12 or newer. ripgrep and fd-find are
optional -- sagent has Python fallbacks when absent -- but recommended
for faster Grep / Glob. PDF rendering uses the bundled pypdfium2
wheel and needs no system install. The Quick Start
above installs the sagent CLI.
Add sagent to your own project as a library:
uv add sagent
Or run from a source checkout:
git clone --depth 1 https://github.com/rekursiv-ai/sagent.git
cd sagent
uv run sagent --help
Bare sagent uses Anthropic and reads ANTHROPIC_API_KEY:
export ANTHROPIC_API_KEY=...
sagent
Pick a different provider by setting its key (see
Provider setup) and passing --provider:
export OPENAI_API_KEY=...
sagent --provider OpenAI
--provider defaults to the first name in --allow-providers, so
SAGENT_ALLOW_PROVIDERS alone picks the default backend and also caps
which providers spawned sub-agents may use:
SAGENT_ALLOW_PROVIDERS=OpenAI sagent # OpenAI is now the default provider
Pipe a prompt on stdin for non-interactive use:
printf 'Say hi in one sentence.' | \
sagent --provider OpenAI --output-format json
Use --continue to resume the most recent session for this working directory, --session PATH for an explicit session directory, or --ephemeral when prompts and auto-memory should not be written to disk. Use --max-budget-usd N to cap API spend for the current run.
See CLI and Sessions for the full flag set.
import asyncio
from sagent import tools
from sagent.agent import Agent
from sagent.lib.custom_json import json_freeze
from sagent.providers import Anthropic
async def main() -> None:
agent = Agent(
model=Anthropic.from_env().model("claude-sonnet-4-6"),
system="You are a concise coding assistant.",
tools=[tools.Read(), tools.Grep(), tools.Glob()],
)
result = await agent.run(json_freeze({"prompt": "Summarize README.md"}))
print(result.content)
asyncio.run(main())
Agent.run() accepts a JSON directive with a prompt key and returns a Message.
See API, Tutorial, and Concepts for more detail.
Sagent ships API-key providers for Anthropic, OpenAI, OpenAISubscription, Google, Moonshot, DashScope, MiniMax, and generic OpenAI-compatible endpoints, a subscription-backed AnthropicCLI that rides your installed claude login, plus a managed local LlamaCpp provider. Set the key (or run the login) for the provider you plan to use:
export ANTHROPIC_API_KEY=...
export OPENAI_API_KEY=...
export GOOGLE_API_KEY=...
export MOONSHOT_API_KEY=...
export DASHSCOPE_API_KEY=...
export MINIMAX_API_KEY=...
and
export SAGENT_ALLOW_PROVIDERS=...
to set the default value of the --provider flag.
| Provider | Environment variable | Example model |
|---|---|---|
Anthropic | ANTHROPIC_API_KEY | claude-sonnet-4-6 |
AnthropicCLI | none (claude auth login --claudeai) | claude-sonnet-4-6 |
OpenAI | OPENAI_API_KEY | gpt-5.6-sol |
Google | GOOGLE_API_KEY | gemini-3.1-pro-preview |
Moonshot | MOONSHOT_API_KEY | kimi-k2.6 |
DashScope | DASHSCOPE_API_KEY | qwen3.6-plus |
MiniMax | MINIMAX_API_KEY | MiniMax-M2.7 |
SelfHosted | none | Qwen/Qwen3.6-27B |
LlamaCpp | none (uses LLAMA_CPP_MODEL + LLAMA_CPP_SERVER) | qwen3.6-27b-12gb |
See Providers for the provider matrix, inference rules, and OpenAI-compatible provider setup.
Install the local runtime extra from a checkout:
uv sync --extra selfhosted
Or add it to your project from PyPI:
uv add "sagent[selfhosted]"
Then pass a HuggingFace repo ID or local snapshot path:
sagent --provider SelfHosted --model Qwen/Qwen3.6-27B+bfloat16+cuda
sagent --provider SelfHosted --model Qwen/Qwen3.6-27B+cuda+bfloat16
For a small smoke test:
sagent --provider SelfHosted --model Qwen/Qwen3-0.6B+float16+cuda \
--effort none --max-response-tokens 32 --max-tool-call-rounds 1
SelfHosted options use + suffixes after the model name. Device, dtype, and compile can appear in any order, but each category can appear once.
The LlamaCpp provider is a second local option: it manages a
llama-server subprocess and talks to it over its OpenAI-compatible
endpoint. Point LLAMA_CPP_SERVER at a built llama-server binary and
LLAMA_CPP_MODEL at a .gguf file, then run
sagent --provider LlamaCpp --model qwen3.6-27b-12gb.
See Self-hosted Models for options, local snapshot paths, and runtime requirements.
The examples/ directory contains small, runnable examples:
offline_custom_tool.py: run an agent/tool/model loop without API keys.decorator_tool.py: wrap a function as a tool.custom_tool.py: implement the full Tool protocol.multi_agent_reviewer.py: spawn an isolated reviewer child.openai_compatible_provider.py: connect an OpenAI-compatible endpoint.Start with the tutorial, then use the examples as copyable patterns. See Examples and Tools.
Sagent is an agent runtime, not a sandbox. Enabled tools run with the current
process permissions: Bash executes local commands, file tools read and write
accessible paths, and provider/network tools send data to their configured
services. Sessions are plaintext local state and may contain prompts, model
responses, tool results, file snippets, and paths.
Use narrow tool sets, pass --ephemeral for one-off sensitive
prompts so sessions and auto-memory are disabled, and run Sagent inside your own
OS/container sandbox when a task needs hard isolation. See
Security.
Not yet in Sagent: MCP, LSP, native sandboxing, desktop UI, tree-sitter repo map, hosted service, browser automation.
This comparison focuses on the runtime shape rather than every feature of each project.
| Sagent | aider | LangChain | OpenClaw | Cline | Claude Code | Codex CLI | Gemini CLI | Flue | Pi | Attractor | npcsh | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Python library | ✅ | 🟡 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Multi-provider | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ |
| Context compaction | ✅ | 🟡 | 🟡 | ❌ | 🟡 | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ |
| User-initiated backend swap | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ |
| Agent-initiated backend swap | ✅ | ❌ | 🟡 | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | 🟡 | ❌ |
| Agent self-mutation | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | 🟡 | ❌ | 🟡 |
| Context hot-swap | ✅ | 🟡 | 🟡 | 🟡 | 🟡 | ❌ | ❌ | ❌ | 🟡 | ✅ | ✅ | ❌ |
| Recursive agent spawn | ✅ | ❌ | ✅ | 🟡 | ❌ | 🟡 | 🟡 | ❌ | ✅ | 🟡 | ✅ | ✅ |
| Multi-agent (fully detached) | ✅ | ❌ | ✅ | ✅ | ❌ | 🟡 | 🟡 | ❌ | ✅ | 🟡 | ✅ | 🟡 |
| GitHub stars (May 2026) | -- | 44.4k | 135.8k | 368.6k | 61.4k | -- | 80.1k | 103.2k | 2.5k | 48.6k | 1.1k | 388 |
✅ = yes, 🟡 = partial, ❌ = no. Corrections welcome -- open a PR.
/model swap, tree-sitter repo map, no multi-agent.session.task() delegation, model chosen per call (no agent-initiated swap), no UI/compaction./reload soft self-mutation, sub-agents opt-in only.sagent (noun, neologism) SAY-jent /ˈseɪ.dʒənt/
From sage + agent.
An AI assistant that confidently performs a task you didn't ask for while ignoring the one you did.
"I asked the sagent to fix one failing test -- it deleted the test and reported all green."
See CONTRIBUTING.md for local validation and public contribution flow.
Sibling projects in the rekursiv-ai family:
mdcat CLI.If you find our work useful, please consider citing:
@misc{rekursivai2026sagent,
title={Sagent - A coding-agent CLI and strongly-typed Python library -- self-mutating, hot-swapping, multi-provider, with async tool calls and bidirectional recursive spawn.},
author={Joshua V. Dillon and Dan Kondratyuk},
year={2026},
howpublished={Github},
url={https://github.com/rekursiv-ai/sagent},
}
Python
100.0%