Open-source AI agent for Microsoft 365.
Project description
███████╗██████╗ ███████╗ █████╗ ███╗ ██╗
██╔════╝██╔══██╗██╔════╝██╔══██╗████╗ ██║
█████╗ ██████╔╝███████╗███████║██╔██╗ ██║
██╔══╝ ██╔══██╗╚════██║██╔══██║██║╚██╗██║
███████╗██║ ██║███████║██║ ██║██║ ╚████║
╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝
Your office. Your rules. Your AI. ...and you, amplified.
Open-source AI agent for Microsoft 365. Apache 2.0. Runs on your laptop against your own tenant. No data leaves unless you tell it to.
Why ersan
If you use Microsoft 365 and want an AI agent that:
- doesn't ship your inbox to a third-party SaaS,
- gates every action through a policy you can read,
- redacts PII and credentials from outbound text,
- works in 60 seconds from
pip install,
then ersan is for you.
Architecture
ersan is the gate (owns the user relationship), not infrastructure (consumed by other agents). Capabilities load as skills, packaged into plugins.
Three pillars:
- Core: pydantic-ai Agent runtime, CLI, REPL
- Policy: trust tiers (
ask_first,do_and_tell,do_it) gating every tool call - Shield: PII / credential redaction on outbound text
(Microsoft Graph OAuth via MSAL ships in v0.4.0 alongside the inbox skills that need it; see Roadmap below.)
In the current runtime, ersan's core is pydantic_ai.Agent. Tool selection
is LLM-driven from natural-language prompts, ersan.policy enforces
tier-based gating via PolicyCapability.wrap_tool_execute(), and
ersan.shield enforces pattern-based blocking plus redaction via
ShieldHook registered on the tool:pre event of
ersan.hooks.HookRegistry, fired by HookBridgeCapability.
Privacy
- No telemetry. Zero phone-home.
- No third-party LLM calls unless your config calls them.
At v0.2.0 this is structurally enforced: local-provider client construction is
covered by
tests/integration/test_local_provider_no_network.py, which proves the provider factories make zero outbound requests during construction. - Your tokens stay local in
~/.ersan/, file-permissions-restricted (POSIX0600/ Windows ACL viaicacls). OS keyring integration is planned for a later release; the file-only path is the supported default and works on every supported OS including headless Linux. - Audit-log to your own sink (
~/.ersan/audit.log).
Platform support
ersan is platform-agnostic by construction (per Constitution §Cross-platform invariants). Every PR runs against:
- Per PR (6 jobs):
ubuntu-latest× Python 3.10 / 3.11 / 3.12 / 3.13;windows-latest× Python 3.13;macos-latest× Python 3.13. - Weekly cron (12 jobs): full cartesian — every supported OS × every supported Python version.
- On every release tag (6 jobs): smoke-install matrix —
{ubuntu, windows, macos} × {Python 3.10, 3.13}runspip installagainst the freshly built wheel before it reaches PyPI via Trusted Publishing with Sigstore PEP 740 attestations.
A wheel does not reach PyPI until the smoke-install matrix passes on all three operating systems.
Roadmap
| Version | Status | Goal |
|---|---|---|
| v0.1.x | ✅ released | Architecture-complete: foundations + eval harness + skill loader |
| v0.2.x | ✅ released | Model Provider Abstraction — ersan.providers, local-first Ollama config, Anthropic config, privacy-invariant provider tests |
| v0.3.0 | ✅ released | Constitutional pillars — bundled slim port of ersan.policy + ersan.shield, wired into the loader with the default retail ask_first policy and ~/.ersan/audit.log audit path |
| v0.4.0 | next | Agent runtime adoption — pydantic_ai.Agent, PolicyCapability, HookBridgeCapability, slim REPL + one-shot CLI |
| v0.5.0 | planned | Inbox MVP — Outlook toolset + Microsoft Graph token storage and MSAL device-code flow |
Configuration
Local Ollama example:
llm:
provider: ollama
model: llama3
base_url: http://localhost:11434/v1
Anthropic example:
llm:
provider: anthropic
model: claude-3-5-sonnet-latest
api_key: ${ANTHROPIC_API_KEY}
v0.2.0 reads ANTHROPIC_API_KEY only. The source codebase's
~/.claude/.credentials.json fallback is not yet ported.
How ersan is built
ersan is maintained by Ersan Bilik with AI coding assistants coordinated through GitHub Spec-Kit. Every release ships through the same cycle: spec → plan → implementation → cross-review → merge → release.
Get involved
- 🐛 Issues
- 📜 Constitution — what ersan is and isn't
- 🤝 Contributing
- 🔒 Security disclosure
- 📜 Code of Conduct
License
Apache 2.0. See LICENSE.
Built by Ersan Bilik.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ersan-0.4.0.tar.gz.
File metadata
- Download URL: ersan-0.4.0.tar.gz
- Upload date:
- Size: 64.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3399a1ef9c68da06ab702ea9dcb1ae053b2eb2572df486085a12774d4efdb3fb
|
|
| MD5 |
76aff550fa51ae4260c490dc0843dfa0
|
|
| BLAKE2b-256 |
30ccbe7cdae25a216928766b4922a428183dfa56e0cb9daf71b89ad3b36006ea
|
Provenance
The following attestation bundles were made for ersan-0.4.0.tar.gz:
Publisher:
release.yml on ersan-ai/ersan
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ersan-0.4.0.tar.gz -
Subject digest:
3399a1ef9c68da06ab702ea9dcb1ae053b2eb2572df486085a12774d4efdb3fb - Sigstore transparency entry: 1340797654
- Sigstore integration time:
-
Permalink:
ersan-ai/ersan@d2fc2f8fc8d684d9cb1efae3f68924d86d14a16e -
Branch / Tag:
refs/tags/ersan-v0.4.0 - Owner: https://github.com/ersan-ai
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d2fc2f8fc8d684d9cb1efae3f68924d86d14a16e -
Trigger Event:
push
-
Statement type:
File details
Details for the file ersan-0.4.0-py3-none-any.whl.
File metadata
- Download URL: ersan-0.4.0-py3-none-any.whl
- Upload date:
- Size: 49.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
43431838ac17bd7bfcb6dd3a5997231a4542c76b083fa590a4545e9f8958758b
|
|
| MD5 |
3fbaa44e2f6395bf08cbf1287fadb867
|
|
| BLAKE2b-256 |
87866265abfec39e5c4d692817ef28b2551680d45dd002b598188b1c9a1652e4
|
Provenance
The following attestation bundles were made for ersan-0.4.0-py3-none-any.whl:
Publisher:
release.yml on ersan-ai/ersan
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ersan-0.4.0-py3-none-any.whl -
Subject digest:
43431838ac17bd7bfcb6dd3a5997231a4542c76b083fa590a4545e9f8958758b - Sigstore transparency entry: 1340797678
- Sigstore integration time:
-
Permalink:
ersan-ai/ersan@d2fc2f8fc8d684d9cb1efae3f68924d86d14a16e -
Branch / Tag:
refs/tags/ersan-v0.4.0 - Owner: https://github.com/ersan-ai
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d2fc2f8fc8d684d9cb1efae3f68924d86d14a16e -
Trigger Event:
push
-
Statement type: