Metadata-Version: 2.4
Name: sentinelos-agent
Version: 1.2.2
Summary: The self-improving AI agent — creates skills from experience, improves them during use, and runs anywhere
Author: Nous Research
License-Expression: MIT
Requires-Python: <3.14,>=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: openai==2.24.0
Requires-Dist: python-dotenv==1.2.2
Requires-Dist: fire==0.7.1
Requires-Dist: httpx[socks]==0.28.1
Requires-Dist: rich==14.3.3
Requires-Dist: tenacity==9.1.4
Requires-Dist: pyyaml==6.0.3
Requires-Dist: ruamel.yaml==0.18.17
Requires-Dist: requests==2.33.0
Requires-Dist: jinja2==3.1.6
Requires-Dist: pydantic==2.13.4
Requires-Dist: prompt_toolkit==3.0.52
Requires-Dist: croniter==6.0.0
Requires-Dist: Markdown==3.10.2
Requires-Dist: PyJWT[crypto]==2.12.1
Requires-Dist: tzdata==2025.3; sys_platform == "win32"
Requires-Dist: psutil==7.2.2
Requires-Dist: pathspec==1.1.1
Requires-Dist: fastapi<1,>=0.104.0
Requires-Dist: uvicorn[standard]<1,>=0.24.0
Requires-Dist: ptyprocess<1,>=0.7.0; sys_platform != "win32"
Requires-Dist: pywinpty<3,>=2.0.0; sys_platform == "win32"
Provides-Extra: anthropic
Requires-Dist: anthropic==0.86.0; extra == "anthropic"
Provides-Extra: exa
Requires-Dist: exa-py==2.10.2; extra == "exa"
Provides-Extra: firecrawl
Requires-Dist: firecrawl-py==4.17.0; extra == "firecrawl"
Provides-Extra: parallel-web
Requires-Dist: parallel-web==0.4.2; extra == "parallel-web"
Provides-Extra: fal
Requires-Dist: fal-client==0.13.1; extra == "fal"
Provides-Extra: edge-tts
Requires-Dist: edge-tts==7.2.7; extra == "edge-tts"
Provides-Extra: modal
Requires-Dist: modal==1.3.4; extra == "modal"
Provides-Extra: daytona
Requires-Dist: daytona==0.155.0; extra == "daytona"
Provides-Extra: hindsight
Requires-Dist: hindsight-client==0.6.1; extra == "hindsight"
Provides-Extra: enterprise
Requires-Dist: python-jose==3.5.0; extra == "enterprise"
Requires-Dist: defusedxml==0.7.1; extra == "enterprise"
Requires-Dist: lxml==6.1.0; extra == "enterprise"
Requires-Dist: signxml==4.5.1; extra == "enterprise"
Provides-Extra: enterprise-rag
Requires-Dist: chromadb>=0.6.0; extra == "enterprise-rag"
Requires-Dist: pypdf>=4.0.0; extra == "enterprise-rag"
Requires-Dist: python-docx>=1.0.0; extra == "enterprise-rag"
Provides-Extra: dev
Requires-Dist: debugpy==1.8.20; extra == "dev"
Requires-Dist: pytest==9.0.2; extra == "dev"
Requires-Dist: pytest-asyncio==1.3.0; extra == "dev"
Requires-Dist: pytest-timeout==2.4.0; extra == "dev"
Requires-Dist: mcp==1.26.0; extra == "dev"
Requires-Dist: starlette==1.0.1; extra == "dev"
Requires-Dist: ty==0.0.21; extra == "dev"
Requires-Dist: ruff==0.15.10; extra == "dev"
Requires-Dist: setuptools==82.0.1; extra == "dev"
Requires-Dist: python-jose==3.5.0; extra == "dev"
Requires-Dist: defusedxml==0.7.1; extra == "dev"
Requires-Dist: lxml==6.1.0; extra == "dev"
Requires-Dist: signxml==4.5.1; extra == "dev"
Provides-Extra: messaging
Requires-Dist: python-telegram-bot[webhooks]==22.6; extra == "messaging"
Requires-Dist: discord.py[voice]==2.7.1; extra == "messaging"
Requires-Dist: aiohttp==3.13.3; extra == "messaging"
Requires-Dist: brotlicffi==1.2.0.1; extra == "messaging"
Requires-Dist: slack-bolt==1.27.0; extra == "messaging"
Requires-Dist: slack-sdk==3.40.1; extra == "messaging"
Requires-Dist: qrcode==7.4.2; extra == "messaging"
Provides-Extra: cron
Provides-Extra: slack
Requires-Dist: slack-bolt==1.27.0; extra == "slack"
Requires-Dist: slack-sdk==3.40.1; extra == "slack"
Requires-Dist: aiohttp==3.13.3; extra == "slack"
Provides-Extra: matrix
Requires-Dist: mautrix[encryption]==0.21.0; extra == "matrix"
Requires-Dist: aiosqlite==0.22.1; extra == "matrix"
Requires-Dist: asyncpg==0.31.0; extra == "matrix"
Requires-Dist: aiohttp-socks==0.11.0; extra == "matrix"
Provides-Extra: wecom
Requires-Dist: defusedxml==0.7.1; extra == "wecom"
Provides-Extra: cli
Requires-Dist: simple-term-menu==1.6.6; extra == "cli"
Provides-Extra: tts-premium
Requires-Dist: elevenlabs==1.59.0; extra == "tts-premium"
Provides-Extra: voice
Requires-Dist: faster-whisper==1.2.1; extra == "voice"
Requires-Dist: sounddevice==0.5.5; extra == "voice"
Requires-Dist: numpy==2.4.3; extra == "voice"
Provides-Extra: pty
Provides-Extra: honcho
Requires-Dist: honcho-ai==2.0.1; extra == "honcho"
Provides-Extra: vision
Requires-Dist: Pillow==12.2.0; extra == "vision"
Provides-Extra: mcp
Requires-Dist: mcp==1.26.0; extra == "mcp"
Requires-Dist: starlette==1.0.1; extra == "mcp"
Provides-Extra: nemo-relay
Requires-Dist: nemo-relay==0.3; extra == "nemo-relay"
Provides-Extra: homeassistant
Requires-Dist: aiohttp==3.13.3; extra == "homeassistant"
Provides-Extra: sms
Requires-Dist: aiohttp==3.13.3; extra == "sms"
Provides-Extra: computer-use
Requires-Dist: mcp==1.26.0; extra == "computer-use"
Requires-Dist: starlette==1.0.1; extra == "computer-use"
Provides-Extra: acp
Requires-Dist: agent-client-protocol==0.9.0; extra == "acp"
Provides-Extra: mistral
Requires-Dist: mistralai==2.4.8; extra == "mistral"
Provides-Extra: bedrock
Requires-Dist: boto3==1.42.89; extra == "bedrock"
Provides-Extra: azure-identity
Requires-Dist: azure-identity==1.25.3; extra == "azure-identity"
Provides-Extra: termux
Requires-Dist: python-telegram-bot[webhooks]==22.6; extra == "termux"
Requires-Dist: sentinelos-agent[cron]; extra == "termux"
Requires-Dist: sentinelos-agent[cli]; extra == "termux"
Requires-Dist: sentinelos-agent[pty]; extra == "termux"
Requires-Dist: sentinelos-agent[mcp]; extra == "termux"
Requires-Dist: sentinelos-agent[honcho]; extra == "termux"
Requires-Dist: sentinelos-agent[acp]; extra == "termux"
Provides-Extra: termux-all
Requires-Dist: sentinelos-agent[termux]; extra == "termux-all"
Requires-Dist: sentinelos-agent[google]; extra == "termux-all"
Requires-Dist: sentinelos-agent[homeassistant]; extra == "termux-all"
Requires-Dist: sentinelos-agent[sms]; extra == "termux-all"
Requires-Dist: sentinelos-agent[web]; extra == "termux-all"
Provides-Extra: dingtalk
Requires-Dist: dingtalk-stream==0.24.3; extra == "dingtalk"
Requires-Dist: alibabacloud-dingtalk==2.2.42; extra == "dingtalk"
Requires-Dist: qrcode==7.4.2; extra == "dingtalk"
Provides-Extra: feishu
Requires-Dist: lark-oapi==1.5.3; extra == "feishu"
Requires-Dist: qrcode==7.4.2; extra == "feishu"
Provides-Extra: google
Requires-Dist: google-api-python-client==2.194.0; extra == "google"
Requires-Dist: google-auth-oauthlib==1.3.1; extra == "google"
Requires-Dist: google-auth-httplib2==0.3.1; extra == "google"
Provides-Extra: youtube
Requires-Dist: youtube-transcript-api==1.2.4; extra == "youtube"
Provides-Extra: web
Requires-Dist: fastapi==0.133.1; extra == "web"
Requires-Dist: uvicorn[standard]==0.41.0; extra == "web"
Requires-Dist: starlette==1.0.1; extra == "web"
Provides-Extra: all
Requires-Dist: sentinelos-agent[cron]; extra == "all"
Requires-Dist: sentinelos-agent[cli]; extra == "all"
Requires-Dist: sentinelos-agent[pty]; extra == "all"
Requires-Dist: sentinelos-agent[mcp]; extra == "all"
Requires-Dist: sentinelos-agent[homeassistant]; extra == "all"
Requires-Dist: sentinelos-agent[sms]; extra == "all"
Requires-Dist: sentinelos-agent[acp]; extra == "all"
Requires-Dist: sentinelos-agent[google]; extra == "all"
Requires-Dist: sentinelos-agent[web]; extra == "all"
Requires-Dist: sentinelos-agent[youtube]; extra == "all"
Dynamic: license-file

<p align="center">
  <img src="assets/banner.png" alt="SentinelOS" width="100%">
</p>

# SentinelOS

**Privacy-first enterprise AI workstation.** Run large language models locally, govern access with RBAC policies, and give every team a secure, auditable AI assistant — without sending a single prompt to the cloud.

SentinelOS combines a desktop chat application, an enterprise governance layer, a local RAG pipeline, and an admin console into a single deployable stack. Models run on your hardware via [Ollama](https://ollama.ai). Every action is logged. Every tool call is policy-gated. Data never leaves your network.

## Key Capabilities

| Capability | Description |
|---|---|
| **Local Model Inference** | Run Qwen, Holo3, Llama, and other open models via Ollama. No API keys required for local models. |
| **RBAC Governance** | Define groups (Engineering, Sales, Legal) with per-group tool policies and model access. |
| **Tool & Model Policies** | Allowlist which MCP tools each group can invoke. Whitelist models with privacy badges (local vs. third-party). |
| **Local RAG** | Ingest company documents (PDF, text). Chunks are embedded and stored locally in ChromaDB. |
| **Admin Console** | Web-based dashboard for group management, model configuration, RAG ingestion, branding, and audit review. |
| **Audit Trail** | Chronological log of every administrative and governance action. |
| **Desktop Application** | Electron-based chat interface with streaming responses, tool output display, and conversation history. |
| **Agent Runtime** | Self-improving agent with skill creation, memory, scheduled automations, and multi-platform messaging. |
| **Default-Deny Security** | Fail-closed policy enforcement, timing-safe token auth, local-first data model. |

## Architecture

```
┌─────────────────────────────────────────────────────────┐
│                     Desktop App                         │
│              (Electron + React + TUI)                   │
└────────────────────┬────────────────────────────────────┘
                     │
┌────────────────────▼────────────────────────────────────┐
│                  Agent Runtime                          │
│     Skills · Memory · Tools · Subagents · Scheduler     │
├─────────────────────────────────────────────────────────┤
│                Enterprise Layer                         │
│  Auth · Groups · Tool Policies · Model Whitelist · RAG  │
│         Governance · Audit · Config Validation          │
├─────────────────────────────────────────────────────────┤
│                  Admin Console                          │
│        (React SPA ← FastAPI backend @ :18830)           │
└────────────────────┬────────────────────────────────────┘
                     │
┌────────────────────▼────────────────────────────────────┐
│              Local Model Backend                        │
│           Ollama (Qwen, Holo3, Llama, …)                │
└─────────────────────────────────────────────────────────┘
```

**Desktop App** — Electron application providing a chat interface, conversation management, and tool output rendering.

**Agent Runtime** — Python-based agent with autonomous skill creation, persistent memory, cron-scheduled tasks, subagent delegation, and multi-platform messaging (Telegram, Discord, Slack, CLI).

**Enterprise Layer** — RBAC identity and governance. Groups define which tools and models each team can access. Policy enforcement is default-deny: if a tool or model isn't explicitly allowed, it's blocked. Auth uses timing-safe token comparison.

**Admin Console** — React single-page application served by a FastAPI backend. Manages groups, tool policies, model whitelists, RAG document ingestion, organization branding, and audit logs.

**Local Model Backend** — Ollama provides local inference. Models are pulled once and served from disk. External providers (OpenRouter, OpenAI) are supported but clearly marked with privacy badges.

## Claude OAuth Architecture (Pro/Max Subscription)

When a user selects **Claude (Pro/Max — OAuth)** during onboarding, SentinelOS
deploys two local sidecars that chain together to route requests through the
user's Anthropic subscription:

```
┌─────────────────────────────────────────────────────────────────┐
│                     Desktop App / Gateway                      │
│              config: base_url = 127.0.0.1:18802                │
└────────────────────────┬────────────────────────────────────────┘
                         │  POST /v1/messages
                         ▼
┌─────────────────────────────────────────────────────────────────┐
│              sentinel-billing-proxy  (:18802)                   │
│                                                                 │
│  • Billing header injection (CC fingerprint)                    │
│  • System prompt template bypass                                │
│  • Tool name obfuscation (bidirectional)                        │
│  • String trigger sanitization                                  │
│  • Tool description stripping                                   │
│  • Property name renaming                                       │
│  • Full bidirectional SSE + JSON reverse mapping                │
│                                                                 │
│  Zero dependencies · Node.js · loopback-only                    │
└────────────────────────┬────────────────────────────────────────┘
                         │  upstream: 127.0.0.1:8316
                         ▼
┌─────────────────────────────────────────────────────────────────┐
│              CLIProxyAPI sidecar  (:8316)                        │
│                                                                 │
│  • OAuth token storage & auto-refresh                           │
│  • Multi-credential pool (round-robin)                          │
│  • Auth file watcher (hot-reload)                               │
│  • Loopback-only binding                                        │
└────────────────────────┬────────────────────────────────────────┘
                         │  HTTPS  (Bearer sk-ant-*)
                         ▼
┌─────────────────────────────────────────────────────────────────┐
│                    api.anthropic.com                             │
│              Claude Opus / Sonnet / Haiku                       │
└─────────────────────────────────────────────────────────────────┘
```

### Sidecar Lifecycle

| Event | Action |
|---|---|
| User selects Claude OAuth in onboarding | `cliproxyapi` binary downloaded + config written |
| Gateway starts with `provider: custom:anthropic-oauth` | `sentinel-billing-proxy` + `cliproxyapi` auto-started |
| OAuth login (`/api/providers/oauth/claude-max/start`) | Browser opens Anthropic consent → tokens saved to auth dir |
| Gateway shutdown | Both sidecars stopped (PID-tracked) |

### File Layout

```
~/.sentinel/
├── bin/cliproxyapi              # managed binary (auto-downloaded)
├── config/cliproxyapi.yaml      # loopback-only config
├── run/cliproxyapi.pid           # PID tracking
├── logs/cliproxyapi.log          # sidecar logs
scripts/
└── billing-proxy/
    ├── proxy.js                  # sentinel-billing-proxy (zero-dep Node.js)
    └── logs/                     # billing proxy logs
```

> **Note:** The sentinel-billing-proxy is independent from any third-party routing
> infrastructure. Each SentinelOS install runs its own instance on loopback.


## Full Install (OAuth routing stack)

Recommended two-command setup:

```bash
pip install sentinelos-agent
sentinel setup
```

When you choose **Claude (Pro/Max — OAuth)**, setup offers to install the full local routing stack automatically. Advanced/manual equivalent:

```bash
sentinel oauth-stack install
```

This installs SentinelOS plus two loopback services:

```
SentinelOS → billing-proxy :18802 → CLIProxyAPI :8316 → Anthropic OAuth
```

### Requirements

- macOS with `launchd` or Linux with user `systemd`
- Node.js 18+
- A Claude Pro/Max OAuth login for CLIProxyAPI

The installer is safe to re-run: existing configs are backed up before replacement, and services are unloaded/reloaded cleanly. To remove services without deleting configs or OAuth credentials:

```bash
./scripts/install-full.sh --uninstall
```

### OAuth login and smoke test

After services start, the installer checks `~/.cli-proxy-api/claude-*.json`. If credentials are missing, interactive terminals can launch:

```bash
cliproxyapi -config ~/.sentinel/cliproxyapi.conf -claude-login
```

When credentials exist, the installer sends a tiny real request through `http://127.0.0.1:18802/v1/messages` and reports pass/fail.

### Troubleshooting

| Symptom | Likely cause | Fix | Logs |
|---|---|---|---|
| `Connection error after 3 retries` or proxy dead after reboot | `sentinel-billing-proxy` or `sentinel-cliproxyapi` service did not load | Re-run `./scripts/install-full.sh`; macOS: `launchctl list | grep sentinel`; Linux: `systemctl --user status sentinel-billing-proxy sentinel-cliproxyapi` | `$SENTINEL_HOME/logs/billing-proxy.err.log`, `$SENTINEL_HOME/logs/cliproxyapi.err.log` |
| HTTP 404 on `/v1/v1/messages` | Client base URL already ends in `/v1`; older proxy did not normalize doubled prefix | Update/re-run installer so `scripts/billing-proxy/proxy.js` normalizes `/v1/v1/` to `/v1/` | `$SENTINEL_HOME/logs/billing-proxy.log` |
| Smoke test skipped | No `~/.cli-proxy-api/claude-*.json` OAuth credential | Run `cliproxyapi -config ~/.sentinel/cliproxyapi.conf -claude-login`, then re-run installer | `$SENTINEL_HOME/logs/cliproxyapi.log` |

## Quick Start

See **[docs/INSTALL.md](docs/INSTALL.md)** for full installation instructions.

```bash
# 1. Run the enterprise setup script
bash sentinel-enterprise/setup.sh

# 2. Start the admin console
SENTINEL_ADMIN_TOKEN=$(cat ~/.sentinel/admin/token) \
  ./venv/bin/python sentinel-enterprise/admin/serve.py

# 3. Open the admin console at http://127.0.0.1:18830
```

## Documentation

- **[Installation Guide](docs/INSTALL.md)** — Prerequisites, setup, configuration
- **[Admin Guide](docs/ADMIN-GUIDE.md)** — Groups, policies, RAG, audit, branding
- **[Architecture](docs/ARCHITECTURE.md)** — Component design, data flows, security model

## License

This project is licensed under the **MIT License** — see [LICENSE](LICENSE) for details.

SentinelOS is a fork of [Nous Research Hermes Agent](https://github.com/NousResearch/hermes-agent). The original work is copyright © 2025 Nous Research and contributors, licensed under MIT. See the LICENSE file for the full copyright notice.
