Metadata-Version: 2.4
Name: mirai-agent
Version: 0.1.1
Summary: Hermes-style personal AI agent powered by the Mirai cloud API
Project-URL: Homepage, https://satuapps.com/agent
Project-URL: Documentation, https://satuapps.com/agent
Project-URL: Repository, https://github.com/satuapps/mirai
Author-email: Mirai <hi@satuapps.com>
License: MIT
Keywords: agent,ai,cli,mirai,tools
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.6
Requires-Dist: pyyaml>=6.0
Provides-Extra: all
Requires-Dist: fastapi>=0.110; extra == 'all'
Requires-Dist: prompt-toolkit>=3.0; extra == 'all'
Requires-Dist: rich>=13.0; extra == 'all'
Requires-Dist: uvicorn[standard]>=0.29; extra == 'all'
Requires-Dist: websockets>=12.0; extra == 'all'
Provides-Extra: cli
Requires-Dist: prompt-toolkit>=3.0; extra == 'cli'
Requires-Dist: rich>=13.0; extra == 'cli'
Provides-Extra: dev
Requires-Dist: fastapi>=0.110; extra == 'dev'
Requires-Dist: prompt-toolkit>=3.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: rich>=13.0; extra == 'dev'
Requires-Dist: uvicorn[standard]>=0.29; extra == 'dev'
Requires-Dist: websockets>=12.0; extra == 'dev'
Provides-Extra: packaging
Requires-Dist: pyinstaller>=6.0; extra == 'packaging'
Provides-Extra: serve
Requires-Dist: fastapi>=0.110; extra == 'serve'
Requires-Dist: uvicorn[standard]>=0.29; extra == 'serve'
Requires-Dist: websockets>=12.0; extra == 'serve'
Description-Content-Type: text/markdown

# Mirai Agent

A Hermes-style personal AI agent (`mirai-agent`, Python 3.11+) living in the
Mirai monorepo. The model backend is the Mirai cloud API
(`https://satuapps.com/v1`, OpenAI-shaped with tool calling and billing);
architecture mirrors [Hermes Agent](https://github.com/NousResearch/hermes-agent):
one agent core, thin I/O shells.

## Docs on the website

Install / quickstart / config / skills / gateway notes (en + id):
**https://satuapps.com/agent**. Desktop installers are shipped via
satuapps.com `/downloads` (CI artifacts uploaded by the operator — not a
public open-source GitHub Releases channel). Self-host with Docker:
see **Distribution → Docker** below.

## Status

- [x] **Phase 1 — agent core**: provider, conversation loop, tool registry
  with 8 built-in tools, permissions, pytest suite.
- [x] **Phase 2 — state, memory, skills**: `state.db` (SQLite + FTS5),
  `SOUL.md` / `MEMORY.md` / `USER.md`, `SKILL.md` skills with progressive
  disclosure, context compression, cron scheduler.
- [x] **Phase 3 — interfaces**: interactive CLI (prompt_toolkit + Rich,
  slash commands) + `mirai-agent serve` (FastAPI WebSocket JSON-RPC with
  the approval flow) for the desktop app — see
  [docs/serve-protocol.md](docs/serve-protocol.md).
- [x] **Phase 4 — Electron desktop**: `desktop/` spawns `mirai-agent serve`,
  safeStorage auth, approvals UI, tray/menus.
- [x] **Phase 5 — packaging**: PyInstaller sidecar, electron-builder
  dmg/nsis, electron-updater, PyPI-ready metadata, CI matrix.
- [x] **Phase 6 — messaging gateway**: `mirai-agent gateway run` asyncio
  daemon with Telegram (long-poll), Discord, Slack Socket Mode, WhatsApp
  Cloud API, and generic HTTP webhook adapters; allowlist auth, per-chat
  sessions, pid/lock/state files, graceful shutdown.
- [x] **Phase 7 — website**: `/agent` page (en/id) + READMEs.
- [x] **Hermes bare-min parity**: MCP client, `delegate_task`, `web_search`,
  `session_search`, `memory` tools, thin pre/post tool hooks — see
  [docs/hermes-parity.md](docs/hermes-parity.md).
- [x] **Web control panel**: `mirai-agent panel` serves a localhost web UI
  (chat, sessions, skills, cron, gateways, MCP, config, usage) reusing the
  serve `/ws` JSON-RPC backend — see [docs/panel.md](docs/panel.md).

## Layout

```
agent/
├── pyproject.toml            # uv-managed, package `mirai-agent` (PyPI-ready)
├── Dockerfile                # single-user self-host image (panel default)
├── docker-compose.yml        # panel + optional serve/gateway profiles
├── packaging/mirai-agent.spec
├── scripts/
│   ├── build_sidecar.sh|.ps1 # PyInstaller onedir for desktop
│   ├── install.sh|.ps1       # end-user install stubs (uv/pip)
│   └── publish.sh            # build sdist/wheel (no upload)
├── docs/serve-protocol.md    # the desktop WebSocket contract (v1)
├── src/mirai_agent/
│   ├── config.py             # MIRAI_API_KEY env → ~/.mirai-agent/config.yaml
│   ├── home.py               # $MIRAI_AGENT_HOME (~/.mirai-agent)
│   ├── permissions.py        # read auto-runs; write/shell need approval
│   ├── core/
│   │   ├── aiagent.py        # AIAgent facade (one core, many shells)
│   │   ├── conversation_loop.py
│   │   └── prompt_builder.py # stable/context/volatile tiers
│   ├── providers/mirai.py    # httpx; JSON tool rounds + simulated SSE
│   ├── tools/                # registry + builtins (files, shell, search,
│   │                         # fetch_url, web_search, memory, session_search,
│   │                         # skill_view, delegate_task) + MCP client
│   ├── hooks.py              # thin pre_tool / post_tool seam
│   ├── state/db.py           # SQLite + FTS5 sessions/messages/usage
│   ├── skills/               # SKILL.md discovery + skill_view tool
│   ├── compression.py        # summarize-oldest-turns compaction
│   ├── cron/                 # jobs.json + background ticker
│   ├── cli/                  # Phase 3: display / slash commands / REPL
│   ├── serve/                # Phase 3: protocol / sessions+approvals /
│   │                         #   FastAPI WS gateway
│   ├── panel/                # Web control panel: FastAPI REST + static UI
│   └── gateway/              # multi-platform runner (pid/lock/state)
│       └── platforms/        # telegram, discord, slack, whatsapp, webhook
└── tests/                    # pytest, fully mocked — no real API calls
```

Parity notes vs Hermes: [docs/hermes-parity.md](docs/hermes-parity.md).

## Quick start (dev)

```bash
cd agent
uv venv && uv pip install -e ".[dev]"   # dev = all extras + pytest
export MIRAI_API_KEY=mirai-…        # or api_key: in ~/.mirai-agent/config.yaml
mirai-agent                                # interactive REPL
mirai-agent -p "what files are in this repo?"  # one-shot
mirai-agent serve                          # headless WS server on :9199
pytest
```

End users install from PyPI:

```bash
uv tool install "mirai-agent[all]"
# or: pip install "mirai-agent[all]"
```

Then run `mirai-agent`, `mirai-agent panel`, or Docker Compose (see
**Distribution** below). The bare install stays minimal (httpx/pydantic/pyyaml —
agent core only); `mirai-agent[cli]` and `mirai-agent[serve]` are the
individual extras. Helpers: [`scripts/install.sh`](scripts/install.sh) /
[`scripts/install.ps1`](scripts/install.ps1).

## Interactive CLI (Phase 3)

prompt_toolkit + Rich: streamed answers, tool/diff rendering, a cost line
per turn, a status bar (model · permission mode · cwd · git branch), and
persistent prompt history. Slash commands:

```
/help              Show help            /skills [name]  List or view skills
/model [name]      Show or set model    /memory         Show MEMORY.md
/clear             Fresh session        /compact        Summarize old history
/cost              Tokens & est. cost   /exit           Quit
```

`/model` persists to `config.yaml`; `/clear` starts a fresh `state.db`
session; write/shell approvals (yes / no / always-project / global) mirror
the Node CLI's UX.

## Headless serve (Phase 3)

```bash
mirai-agent serve [--host 127.0.0.1] [--port 9199] [--yolo|--read-only]
```

FastAPI + uvicorn. `GET /health` is the readiness probe;
`ws://<host>:<port>/ws` speaks **versioned JSON-RPC 2.0 (`protocol: 1`)**:
methods `ping`, `server.info`, `chat.send` (streams `chat.chunk` +
`chat.tool_*` events), `tool.approval_response`, `sessions.list`,
`sessions.get`; events `server.hello` and `tool.approval_request` (the
client answers with once / always-project / always-global / deny).
Sessions live in the same `state.db`, so a desktop session can be resumed
from the CLI and vice versa. Full contract with message shapes and
examples: **[docs/serve-protocol.md](docs/serve-protocol.md)**.

## Web control panel

`mirai-agent panel` serves a localhost web UI on top of the same `/ws`
JSON-RPC backend the desktop app uses — so you can manage your local
agent from a browser without the Electron app.

```bash
mirai-agent panel                 # http://127.0.0.1:9200
mirai-agent panel --port 8080     # custom port
```

Sections: **Chat** (streaming + tool/approval UI over `/ws`), **Sessions**
(list / search / resume / delete), **Skills** (list / view), **Cron** (add /
run now / toggle / delete), **Gateways** (per-platform status, start/stop,
allowlist), **MCP servers** (add / remove / test), **Config** (edit
`config.yaml` safely, API key redacted), **Usage/cost** (from `state.db`).

The built UI ships inside the wheel, so `mirai-agent panel` works out of
the box. To rebuild from source: `cd agent/panel && npm install && npm run
build` (writes `src/mirai_agent/panel/static/`). Security: binds to
`127.0.0.1` only by default; a non-loopback bind requires `panel.token` in
`config.yaml` and presents that bearer on every request. Full notes:
**[docs/panel.md](docs/panel.md)**.

## Key design facts

- **The Mirai API replaces client system prompts** with its brand prompt
  server-side. Agent identity (SOUL.md), project context (MIRAI.md /
  AGENTS.md), and memory therefore travel **inside the first user message**
  of each session (`<agent_context>…</agent_context>`), handled by
  `core/prompt_builder.py`.
- **Tool rounds are non-stream JSON**; plain assistant text comes back as
  simulated SSE. The provider tolerates both transparently.
- **402 means top up** — surfaced as `PaymentRequiredError` with the
  subscription URL.
- Permissions: `read` tools run freely; `write`/`shell` ask (once / always
  project / always global / deny), with `--yolo`/`--auto` and `--read-only`
  modes. Allowlists live in `.mirai/permissions.json` (project) and
  `~/.mirai-agent/permissions.json` (global).

## Messaging gateway

`mirai-agent gateway run` starts an asyncio daemon that connects every
configured platform, authorizes chats against per-platform allowlists,
maps each chat to a persistent `state.db` session (`{platform}-{chat_id}`),
runs the message through the shared `AIAgent` core, and delivers the reply.

```bash
mirai-agent gateway run            # foreground daemon (Ctrl-C to stop)
mirai-agent gateway status         # pid / state / platforms / offsets
```

Config (`~/.mirai-agent/config.yaml`) — enable any subset:

```yaml
gateway:
  permission_mode: read-only       # SAFE DEFAULT for chat surfaces
  telegram:
    bot_token: "123456:ABC..."     # or MIRAI_TELEGRAM_BOT_TOKEN
    allow_from: [123456789, "@you"]
  discord:
    bot_token: "…"                 # or MIRAI_DISCORD_BOT_TOKEN (needs websockets)
    allow_from: ["1234567890"]
  slack:
    bot_token: "xoxb-…"            # MIRAI_SLACK_BOT_TOKEN
    app_token: "xapp-…"            # MIRAI_SLACK_APP_TOKEN (Socket Mode)
    allow_from: ["U123", "C456"]
  whatsapp:                        # Meta Cloud API (not Baileys)
    access_token: "…"              # MIRAI_WHATSAPP_ACCESS_TOKEN
    phone_number_id: "…"           # MIRAI_WHATSAPP_PHONE_NUMBER_ID
    verify_token: "mirai-verify"
    webhook_port: 8787
    allow_from: ["15551234567"]
  webhook:                         # generic HTTP POST → agent → callback
    host: "127.0.0.1"
    port: 8788
    path: "/hook"
    secret: "…"                    # Bearer / X-Mirai-Secret
    allow_from: ["*"]
```

Also useful agent features (Hermes bare-min):

```yaml
mcp_servers:
  filesystem:
    command: npx
    args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
```

Built-in tools added for parity: `web_search`, `session_search`, `memory`,
`delegate_task`. Thin `pre_tool` / `post_tool` hooks live in
`mirai_agent.hooks`.

Behavior notes:

- **Authorization** — only `allow_from` chats reach the agent (webhook may
  use `"*"`). Match by ID or username; empty allowlist denies everyone.
- **Sessions** — deterministic `{platform}-{chat_id}` rows in `state.db`.
- **Permissions** — gateway defaults to `read-only`; opt in via
  `gateway.permission_mode: auto` or allowlists in `permissions.json`.
- **Process files** under `~/.mirai-agent/`: `gateway.pid`, `gateway.lock`,
  `gateway_state.json`.
- **Discord / Slack** need the `websockets` package (`mirai-agent[serve]` /
  `[all]` / `[dev]`).
- Deferred (not bare-min): email, Baileys WhatsApp, browser/vision/TTS,
  ACP, full dashboard — see [docs/hermes-parity.md](docs/hermes-parity.md).

## Distribution (Phase 5)

### Docker (single-user self-host)

Image runs the web panel by default; compose profiles add headless
`serve` and/or the messaging `gateway`. State is a volume at `/data`
(`MIRAI_AGENT_HOME`). Non-loopback panel binds need `MIRAI_PANEL_TOKEN`.

```bash
cd agent
export MIRAI_API_KEY=mirai-…
export MIRAI_PANEL_TOKEN=$(openssl rand -hex 16)
docker compose up --build
# Panel: http://127.0.0.1:9200/?token=$MIRAI_PANEL_TOKEN

# Optional:
docker compose --profile serve up          # + :9199 JSON-RPC
docker compose --profile gateway up        # messaging daemon
```

Files: [`Dockerfile`](Dockerfile), [`docker-compose.yml`](docker-compose.yml).
This is **single-user** self-host (your key, your volume) — not a
multi-tenant cloud control plane.

### Terminal users (PyPI)

Package name: **`mirai-agent`** — live on
[PyPI](https://pypi.org/project/mirai-agent/).

```bash
uv tool install "mirai-agent[all]"
# or:
pip install "mirai-agent[all]"
```

Then:

```bash
export MIRAI_API_KEY=mirai-…
mirai-agent                 # interactive CLI
mirai-agent panel           # localhost web UI (:9200)
```

Docker Compose is under **Docker** above. Helpers:
[`scripts/install.sh`](scripts/install.sh) /
[`scripts/install.ps1`](scripts/install.ps1).

Maintainers — publish a new version:

```bash
cd agent
uv build                         # or: bash scripts/publish.sh
uv publish                       # wheel + sdist under dist/
```

### Desktop sidecar (PyInstaller)

Builds a **onedir** binary with `[cli]` + `[serve]` extras for Electron
`extraResources`:

```bash
bash scripts/build_sidecar.sh    # macOS / Linux → dist/mirai-agent/mirai-agent
# Windows: .\scripts\build_sidecar.ps1 → mirai-agent.exe
```

Architecture is **native to the builder** (Apple Silicon arm64 on
`macos-14`, x64 on `windows-latest` / `ubuntu-latest`). Spec:
[`packaging/mirai-agent.spec`](packaging/mirai-agent.spec). Staged copy
lands in `desktop/resources/mirai-agent/` for electron-builder.

See [`desktop/README.md`](../desktop/README.md) for dmg / nsis /
AppImage / deb, satuapps.com download hosting
(`tools/publish-desktop-artifacts.sh`), and CI
(`.github/workflows/desktop.yml`).