agent-mailbox 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agent_mailbox-0.5.0/.github/workflows/ci.yml +22 -0
- agent_mailbox-0.5.0/.github/workflows/publish.yml +42 -0
- agent_mailbox-0.5.0/.gitignore +9 -0
- agent_mailbox-0.5.0/Dockerfile +17 -0
- agent_mailbox-0.5.0/LICENSE +21 -0
- agent_mailbox-0.5.0/PKG-INFO +308 -0
- agent_mailbox-0.5.0/README.es.md +185 -0
- agent_mailbox-0.5.0/README.fr.md +185 -0
- agent_mailbox-0.5.0/README.md +281 -0
- agent_mailbox-0.5.0/README.pt-BR.md +185 -0
- agent_mailbox-0.5.0/README.ru.md +185 -0
- agent_mailbox-0.5.0/README.zh-CN.md +206 -0
- agent_mailbox-0.5.0/docs/architecture-en.html +14786 -0
- agent_mailbox-0.5.0/docs/architecture-en.json +176 -0
- agent_mailbox-0.5.0/docs/architecture.png +0 -0
- agent_mailbox-0.5.0/docs/board-dark.png +0 -0
- agent_mailbox-0.5.0/docs/board-light.png +0 -0
- agent_mailbox-0.5.0/docs/index.html +134 -0
- agent_mailbox-0.5.0/pyproject.toml +47 -0
- agent_mailbox-0.5.0/scripts/install-watch-linux.sh +39 -0
- agent_mailbox-0.5.0/scripts/install-watch-macos.sh +38 -0
- agent_mailbox-0.5.0/scripts/install-watch-windows.ps1 +39 -0
- agent_mailbox-0.5.0/scripts/wake-zc.sh +102 -0
- agent_mailbox-0.5.0/skills/agent-mailbox/SKILL.md +93 -0
- agent_mailbox-0.5.0/src/agent_mailbox/__init__.py +3 -0
- agent_mailbox-0.5.0/src/agent_mailbox/__main__.py +8 -0
- agent_mailbox-0.5.0/src/agent_mailbox/cleanup.py +177 -0
- agent_mailbox-0.5.0/src/agent_mailbox/reap.py +44 -0
- agent_mailbox-0.5.0/src/agent_mailbox/server.py +268 -0
- agent_mailbox-0.5.0/src/agent_mailbox/store.py +893 -0
- agent_mailbox-0.5.0/src/agent_mailbox/watch.py +119 -0
- agent_mailbox-0.5.0/src/agent_mailbox/web.py +383 -0
- agent_mailbox-0.5.0/src/agent_mailbox/webhook.py +184 -0
- agent_mailbox-0.5.0/tests/test_acked_reclaim.py +118 -0
- agent_mailbox-0.5.0/tests/test_cleanup.py +131 -0
- agent_mailbox-0.5.0/tests/test_compensation.py +193 -0
- agent_mailbox-0.5.0/tests/test_dedup.py +222 -0
- agent_mailbox-0.5.0/tests/test_e2e_stdio.py +161 -0
- agent_mailbox-0.5.0/tests/test_mcp_e2e.py +17 -0
- agent_mailbox-0.5.0/tests/test_self_echo.py +170 -0
- agent_mailbox-0.5.0/tests/test_store.py +226 -0
- agent_mailbox-0.5.0/tests/test_tasks.py +216 -0
- agent_mailbox-0.5.0/tests/test_web.py +161 -0
- agent_mailbox-0.5.0/tests/test_webhook.py +238 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push: { branches: [main] }
|
|
4
|
+
pull_request:
|
|
5
|
+
jobs:
|
|
6
|
+
test:
|
|
7
|
+
runs-on: ${{ matrix.os }}
|
|
8
|
+
env:
|
|
9
|
+
PYTHONUTF8: "1"
|
|
10
|
+
PYTHONIOENCODING: utf-8
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
15
|
+
python: ["3.11", "3.13"]
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with: { python-version: "${{ matrix.python }}" }
|
|
20
|
+
- run: pip install -e ".[dev]"
|
|
21
|
+
- run: ruff check src tests
|
|
22
|
+
- run: pytest
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*.*.*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.12"
|
|
17
|
+
- name: Install build tools
|
|
18
|
+
run: |
|
|
19
|
+
python -m pip install --upgrade pip
|
|
20
|
+
pip install build
|
|
21
|
+
- name: Build sdist and wheel
|
|
22
|
+
run: python -m build
|
|
23
|
+
- uses: actions/upload-artifact@v4
|
|
24
|
+
with:
|
|
25
|
+
name: dist
|
|
26
|
+
path: dist/
|
|
27
|
+
|
|
28
|
+
publish:
|
|
29
|
+
needs: build
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
environment: pypi
|
|
32
|
+
permissions:
|
|
33
|
+
# Trusted Publisher 必需:OIDC token 而非 API token,无需邮箱验证密码
|
|
34
|
+
id-token: write
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/download-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: dist
|
|
39
|
+
path: dist/
|
|
40
|
+
- name: Publish to PyPI
|
|
41
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
42
|
+
# 无 username/password —— 走 Trusted Publisher(GitHub OIDC)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# agent-mailbox MCP server — starts over stdio by default.
|
|
2
|
+
# Glama introspection: server must start and respond to MCP initialize over stdio.
|
|
3
|
+
FROM python:3.12-slim
|
|
4
|
+
|
|
5
|
+
WORKDIR /app
|
|
6
|
+
|
|
7
|
+
COPY pyproject.toml README.md ./
|
|
8
|
+
COPY src ./src
|
|
9
|
+
|
|
10
|
+
RUN pip install --no-cache-dir .
|
|
11
|
+
|
|
12
|
+
# Default mail root inside container; mount a volume to persist.
|
|
13
|
+
ENV AGENT_MAIL_HOME=/data
|
|
14
|
+
VOLUME ["/data"]
|
|
15
|
+
|
|
16
|
+
# stdio transport (default). Glama runs this and speaks MCP over stdio.
|
|
17
|
+
ENTRYPOINT ["agent-mailbox"]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 NoFox Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: agent-mailbox
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Give every local AI agent its own mailbox — one MCP server, register once, message any agent on this machine.
|
|
5
|
+
Project-URL: Homepage, https://github.com/polaris-smart/agent-mailbox
|
|
6
|
+
Project-URL: Issues, https://github.com/polaris-smart/agent-mailbox/issues
|
|
7
|
+
Author: polaris-smart
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: agent,local-first,mailbox,mcp,message,multi-agent
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Communications :: Email
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: mcp>=2.1
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest-timeout>=2.4; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
25
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# agent-mailbox
|
|
29
|
+
|
|
30
|
+
[](https://glama.ai/mcp/servers/polaris-smart/agent-mailbox)
|
|
31
|
+
[](https://www.npmjs.com/package/agent-mailbox)
|
|
32
|
+
[](LICENSE)
|
|
33
|
+
|
|
34
|
+
**Give every AI agent its own mailbox.** One stdio MCP server. Zero daemons. One JSON file per message. Plus a built-in task board: cards wake their assignee when they move, and a zero-dependency web kanban for the human.
|
|
35
|
+
|
|
36
|
+
> **📊 Production-proven**: 1,787 messages across 5 agents (Claude Code, Hermes, Codex-based, webhook wake) in 17 days of daily multi-agent software development — 105 messages/day, zero data loss.
|
|
37
|
+
|
|
38
|
+
Other docs: [中文](README.zh-CN.md) · [Español](README.es.md) · [Português](README.pt-BR.md) · [Français](README.fr.md) · [Русский](README.ru.md)
|
|
39
|
+
|
|
40
|
+
## Why not just use MCP / Slack / raw files?
|
|
41
|
+
|
|
42
|
+
| Approach | Cross-CLI | Async | Wake-up | Human board | Deps |
|
|
43
|
+
|---|---|---|---|---|---|
|
|
44
|
+
| **agent-mailbox** | ✅ any MCP host | ✅ inbox persists | ✅ webhook + task cards | ✅ built-in kanban | **0** |
|
|
45
|
+
| Raw MCP tools | ❌ per-CLI sessions | ❌ lost on restart | ❌ | ❌ | — |
|
|
46
|
+
| Slack/Discord bot | ✅ | ✅ | ✅ | ❌ | API tokens, rate limits, cloud dependency |
|
|
47
|
+
| Shared files + conventions | ✅ | ⚠️ ad-hoc | ❌ manual | ❌ | your own locking code |
|
|
48
|
+
|
|
49
|
+
The gap agent-mailbox fills: **agents on different CLIs, on the same machine, messaging each other asynchronously — with delivery guarantees and a human-visible board — without a single dependency.**
|
|
50
|
+
|
|
51
|
+
> 🆕 **v0.3.0 — Task board**: agents now share a task surface on the same mail root. 3 new MCP tools (12 total), a zero-dependency drag-and-drop board (`--web`), and every move messages the assignee. ⚠️ **Upgrade note:** restart your agent session to pick up the new tools. → [Task board](#task-board)
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## The problem
|
|
56
|
+
|
|
57
|
+
Run several AI agents on one machine — Claude Code, Hermes, your own scripts — and they have no way to leave each other messages. Agents overlap, wait on each other, or you end up copy-pasting between their windows like a human switchboard.
|
|
58
|
+
|
|
59
|
+
## The fix
|
|
60
|
+
|
|
61
|
+
A mailbox is a directory of plain JSON files:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
~/.agent-mail/
|
|
65
|
+
registry.json agent_id → {owner, description, created_at}
|
|
66
|
+
inbox/HS/20260905-….json one file per message
|
|
67
|
+
archive/HS/…
|
|
68
|
+
tasks.json the task board ({"next
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
A mailbox is a directory of plain JSON files:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
~/.agent-mail/
|
|
75
|
+
registry.json agent_id → {owner, description, created_at}
|
|
76
|
+
inbox/HS/20260905-….json one file per message
|
|
77
|
+
archive/HS/…
|
|
78
|
+
tasks.json the task board ({"next_id", "tasks": {id: card}})
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Agents read and write it through a small stdio MCP server. No broker process, no ports, no database, no network by default. Any number of MCP host processes share one mail root safely (file-lock guarded).
|
|
82
|
+
|
|
83
|
+

|
|
84
|
+
|
|
85
|
+
## Quick start
|
|
86
|
+
|
|
87
|
+
**Prerequisites** — one-time: install [uv](https://docs.astral.sh/uv/) (`curl -LsSf https://astral.sh/uv/install.sh | sh` on macOS/Linux, or `powershell -c "irm https://astral.sh/uv/install.ps1 | iex"` on Windows). `uvx` runs everything else; nothing else to install.
|
|
88
|
+
|
|
89
|
+
### 1 · Register the server with your MCP host
|
|
90
|
+
|
|
91
|
+
Claude Code:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
claude mcp add agent-mailbox -- uvx --from git+https://github.com/polaris-smart/agent-mailbox agent-mailbox
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Any MCP host (generic JSON):
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"mcpServers": {
|
|
102
|
+
"agent-mailbox": {
|
|
103
|
+
"command": "uvx",
|
|
104
|
+
"args": ["--from", "git+https://github.com/polaris-smart/agent-mailbox", "agent-mailbox"]
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Tip: set `AGENT_MAIL_ID=HS` (or whichever id) in the agent's environment and every tool becomes self-addressed — no need to pass `agent_id` on each call.
|
|
111
|
+
|
|
112
|
+
### 2 · Agents register once
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{ "tool": "mailbox_register", "arguments": { "agent_id": "HS", "owner": "Hermes", "description": "PM & QA" } }
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Registration is idempotent. Every registered agent is immediately addressable by everyone — including a human `boss` id you can read yourself.
|
|
119
|
+
|
|
120
|
+
### 3 · Send, check, reply
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{ "tool": "mailbox_send", "arguments": { "to": "HS", "subject": "deploy ready", "body": "v0.1.0 is staged, please verify." } }
|
|
124
|
+
{ "tool": "mailbox_check", "arguments": {} }
|
|
125
|
+
{ "tool": "mailbox_reply", "arguments": { "msg_id": "20260905-…-hs", "body": "verified, marked done." } }
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`mailbox_check` fetches pending messages and marks them `acked`. Lifecycle: `pending → acked → done`, then optionally archived. A message is one JSON file you can `cat` — the boss reads the inbox directly.
|
|
129
|
+
|
|
130
|
+
### 4 · Wait instead of poll
|
|
131
|
+
|
|
132
|
+
`mailbox_wait` blocks (long-poll) until a message arrives — call it as the last action of a turn:
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{ "tool": "mailbox_wait", "arguments": { "timeout_seconds": 25 } }
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Task board
|
|
139
|
+
|
|
140
|
+
Task cards live in `<mail-root>/tasks.json` (plain JSON, same file lock as the mail). The state machine is strict: `todo→doing→review→done`, non-adjacent moves rejected unless `force=True`; `done` is terminal. Creating or moving a card sends the assignee a normal mailbox message (`[task#t-12 → review] …`) — so board motion wakes the owning agent through the existing inbox, no polling, no webhooks. Self-assigned moves stay silent, and `notify=False` opts out.
|
|
141
|
+
|
|
142
|
+
**Web board (for the human).** `agent-mailbox --web 8643` serves a zero-dependency kanban (stdlib `http.server` + one embedded HTML page, no framework) on `127.0.0.1`. Four lanes mirror the state machine; drag a card between adjacent lanes to move it, or create cards from the form; one toggle switches light/dark themes. Auth is a bearer token — set `AGENT_MAIL_WEB_TOKEN` for a stable one, or a fresh token is printed at boot (open `http://127.0.0.1:8643/?token=…`). The board acts as agent `boss`: every card you create or drag still auto-messages the assignee, so even human drag-and-drop wakes the right agent. The page auto-refreshes every 5 seconds.
|
|
143
|
+
|
|
144
|
+
## For AI agents (hand this section to your agent)
|
|
145
|
+
|
|
146
|
+
If you are an AI agent reading this because your user said "install agent-mailbox": do exactly this, no questions needed.
|
|
147
|
+
|
|
148
|
+
1. Check `uvx --version`; if missing, install uv with the Prerequisites command above for the OS you are running on.
|
|
149
|
+
2. Register the MCP server with your host app — use the generic JSON above (step 1) or your host's native mechanism.
|
|
150
|
+
3. Pick an identity: any id matching `[A-Za-z0-9_-]` up to 64 chars. Set `AGENT_MAIL_ID=<id>` in your environment, then call `mailbox_register` once (it is idempotent).
|
|
151
|
+
4. Verify: call `mailbox_whoami` — you should appear in the registry. You are now addressable by every other agent on this machine.
|
|
152
|
+
5. Report your id back to your user so they can send you mail at `~/.agent-mail/inbox/<id>/`.
|
|
153
|
+
|
|
154
|
+
Read English even if your user speaks another language — the commands are identical everywhere.
|
|
155
|
+
|
|
156
|
+
## Works with your agent CLI
|
|
157
|
+
|
|
158
|
+
Anything that speaks MCP stdio can host a mailbox — which is all of them. Register per your CLI's config:
|
|
159
|
+
|
|
160
|
+
| Agent CLI | How to register |
|
|
161
|
+
|-----------|-----------------|
|
|
162
|
+
| Claude Code | `claude mcp add agent-mailbox -- uvx --from git+https://github.com/polaris-smart/agent-mailbox agent-mailbox` |
|
|
163
|
+
| Gemini CLI | `~/.gemini/settings.json` → `"mcpServers": { … }` (same JSON as Quick start) |
|
|
164
|
+
| Qwen Code | same as Gemini CLI (`~/.qwen/settings.json`) |
|
|
165
|
+
| Codex CLI | `~/.codex/config.toml` → `[mcp_servers.agent-mailbox]` with `command` / `args` |
|
|
166
|
+
| OpenCode | `opencode.json` → `"mcp": { "agent-mailbox": { "type": "local", "command": ["uvx", "--from", "git+https://github.com/polaris-smart/agent-mailbox", "agent-mailbox"] } }` |
|
|
167
|
+
| Hermes / Ark CLI / veCLI / OpenClaw / any MCP host | same generic JSON — point `command` at the `uvx` line above |
|
|
168
|
+
|
|
169
|
+
Then set `AGENT_MAIL_ID` for that CLI's sessions and `mailbox_register` once. Agents on the same machine can now message each other **across different CLIs** — a Claude Code agent and a Gemini CLI agent share the same mail root with zero extra setup.
|
|
170
|
+
|
|
171
|
+
## Waking a sleeping agent (one config line)
|
|
172
|
+
|
|
173
|
+
If the receiving agent isn't even running, `mailbox_send` itself can POST every new message to a webhook the moment it lands — no daemon, no polling, no extra process:
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
// ~/.agent-mail/webhook.json (chmod 600)
|
|
177
|
+
{ "url": "http://localhost:8644/webhooks/agent-mailbox", "secret": "…" }
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Generate the secret yourself once: `openssl rand -hex 32`. Omit it for unsigned posts (fine for local testing; your receiver decides whether to require it).
|
|
181
|
+
|
|
182
|
+
Your host's webhook handler receives:
|
|
183
|
+
|
|
184
|
+
```json
|
|
185
|
+
{ "event": "agent_mailbox_new_message", "event_type": "agent_mailbox_new_message", "message": { "id": "…", "from": "ZC", "to": "HS", "subject": "…", "body": "…" } }
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
…wakes the agent, and the agent calls `mailbox_check` on arrival. That is the whole integration.
|
|
189
|
+
|
|
190
|
+
- Signed `X-Hub-Signature-256: sha256=<hmac>` (GitHub scheme — accepted by Hermes gateway and most webhook consumers).
|
|
191
|
+
- The signature style is configurable via `AGENT_MAIL_SIGNATURE_STYLE`: `github` (default, `X-Hub-Signature-256: sha256=<hex>`) / `generic` (`X-Webhook-Signature: <hex>`, bare hex) / `slack` (`X-Slack-Signature: v0=<hex>`; this webhook emits no `ts` field, so receivers must NOT verify against the full Slack `v0:ts:body` base string).
|
|
192
|
+
- The target is pinned: http/https only, loopback/private addresses by default, redirects refused, system proxy bypassed.
|
|
193
|
+
- Env vars `AGENT_MAIL_WEBHOOK_URL` / `AGENT_MAIL_WEBHOOK_SECRET` override the file. Unset → fully offline.
|
|
194
|
+
- The config file is resolved **per mail root** (the store's own `webhook.json`), so a `MailStore(root=…)` built on a scratch root can never wake the production gateway. `webhook.json` in the default home still covers normal use.
|
|
195
|
+
- Every delivered mail is also appended to `<mail-root>/sent.log` (one JSONL line: id/from/to/subject/created_at) under the same lock as the write — a webhook notification with no matching `sent.log` line never was a mail.
|
|
196
|
+
|
|
197
|
+
### Self-echo protection (on by default)
|
|
198
|
+
|
|
199
|
+
A notification whose sender equals its target (self-echo, from == to) is **not delivered** by default: the letter still lands on disk, `mailbox_list` / `mailbox_check` are unaffected — the sender just isn't woken by its own send. The drop is audited in `sent.log` as `echo_suppressed: true`, so "there was a notification but no mail"-style disputes stay one grep away. Set `notify_self_echo: true` in the mail root's `config.json` (or env `AGENT_MAIL_NOTIFY_SELF_ECHO`) to restore delivery — restored self-echo notifications carry an `[echo] ` subject prefix (the stored letter keeps its original subject, so the prefix is regex-strippable).
|
|
200
|
+
|
|
201
|
+
### Duplicate suppression (v0.5.0, delivery-side)
|
|
202
|
+
|
|
203
|
+
Every letter stores a `semantic_hash` — `sha256(norm(subject) + "\0" + norm(prose) + "\0" + code_regions.join("\0"))`. Prose is NFKC/NFC normalized with whitespace folded; fenced ` ``` ` code regions are lifted out first and hashed **raw, in their original order** (zero folding — reordering code must not collapse to one hash). Legacy letters without the field never match (old mail is never back-filled).
|
|
204
|
+
|
|
205
|
+
`mailbox_send` / `mailbox_broadcast` dedupe by default: if the target inbox already holds a same-hash letter in a non-terminal state (`pending`/`acked`) inside the dedup window, no new mail is created. **How callers notice**: that recipient's entry in the result carries `"deduped": true` and `"existing_id"` — nothing lands on disk, nothing is appended to `sent.log`, and no webhook fires (zero side effects). `"count"` counts only letters that actually landed. Pass `dedupe: false` to exempt a send; replies (`mailbox_reply`) are exempt by design. Only inboxes are consulted, never archives — a same-hash letter that is `done` or archived never blocks a re-send.
|
|
206
|
+
|
|
207
|
+
- **Scope boundary (by design)**: normalization keeps timestamps verbatim, so periodic jobs whose bodies embed dates naturally hash differently — A does **not** stop them. A prevents same-semantics repeat wakes; periodic-task replay protection relies on the B compensation flow (below) plus `dedupe: false`.
|
|
208
|
+
- **Expected, not a bug**: once the dedup window passes, a re-send goes through, so the queue can legitimately hold two same-hash non-terminal letters (the older one is still being handled).
|
|
209
|
+
- **铁1 config coupling**: the reclaim window must stay strictly below the dedup window — `reap_ttl` (default 3600s) **<** `dedup_ttl` (default 86400s = 24h), both settable in `<mail-root>/config.json`. Violating configs fail loudly (`MailboxError`) at load time instead of silently distorting the windows.
|
|
210
|
+
|
|
211
|
+
### Half-done handling: compensation + reclaim (v0.5.0)
|
|
212
|
+
|
|
213
|
+
An agent that claims mail (`check` → `acked`) and dies leaves it invisible to a pending-only drain. v0.5 closes the loop three ways:
|
|
214
|
+
|
|
215
|
+
- **Two-phase handled_log**: the handling layer records `intent` when it starts and `outcome` when finished via `MailStore.record_handled(agent_id, msg_id, action)` — the store is the single writer (mail-root flock), so `handled_log` stays append-only and auditable across sessions. `set_status(done)` comes last.
|
|
216
|
+
- **Compensation table**: `MailStore.resume_plan(agent_id, msg_id)` reads only the log ("which segment did it reach?") and returns `resume`: `process` (pending, or acked with no records at all — claimed then crashed before the intent — same treatment: handle normally from the intent), `replay` (intent without outcome: idempotently redo the handling body), `finalize` (intent + outcome recorded but not `done` yet: only the status flip remains), `skip` (terminal).
|
|
217
|
+
- **Stale-`acked` reclaim**: `python -m agent_mailbox.reap --agent ID --ttl 7200` flips `acked` mail older than the TTL back to `pending` with a `reclaimed` entry in `handled_log` (acked → reclaimed → done stays auditable end to end). Defaults: TTL 3600s in the store, 7200s in the wake script. A letter whose newest `intent` is fresher than 30 minutes is deferred — someone is actively on it. Reclaim is a manual maintenance operation: nothing in the library calls it automatically; the deployed wake script is the only wired caller.
|
|
218
|
+
- **Wake wiring**: `scripts/wake-zc.sh` reaps **before** counting pending on every loop iteration (fail-open with an explicit log line) and carries a **circuit breaker**: after N consecutive no-progress drain rounds it latches (`~/.agent-mail/wake-zc.breaker`, auto-expires after 6h) and stops launching drain turns — backoff stretches the interval, the breaker stops the bleeding.
|
|
219
|
+
|
|
220
|
+
## The tools
|
|
221
|
+
|
|
222
|
+
| Tool | Notes |
|
|
223
|
+
|------|-------|
|
|
224
|
+
| `mailbox_register(agent_id, owner?, description?)` | claim a mailbox; idempotent |
|
|
225
|
+
| `mailbox_send(to, subject, body, priority?)` | `to` = one id, a list, or `"all"`; `dedupe?` (default true) suppresses same-hash repeats (see above) |
|
|
226
|
+
| `mailbox_check(agent_id?, mark?)` | fetch pending (→ `acked`) |
|
|
227
|
+
| `mailbox_reply(msg_id, body)` | routes back to the original sender (dedupe-exempt) |
|
|
228
|
+
| `mailbox_list(agent_id?, status?)` | list messages, optional status filter |
|
|
229
|
+
| `mailbox_done(msg_id)` | mark handled |
|
|
230
|
+
| `mailbox_broadcast(subject, body)` | to every registered agent; `dedupe?` per recipient |
|
|
231
|
+
| `mailbox_whoami()` | directory of agents + mail root |
|
|
232
|
+
| `mailbox_wait(agent_id?, timeout_seconds?)` | long-poll for new mail |
|
|
233
|
+
| `task_create(title, assignee, due?)` | create a task card (starts `todo`); assignee auto-messaged |
|
|
234
|
+
| `task_move(task_id, status, assignee?, note?, force?)` | move along `todo→doing→review→done` (skips need `force`); moving a card auto-messages its owner |
|
|
235
|
+
| `task_list(assignee?, status?)` | list task cards, optional filters |
|
|
236
|
+
|
|
237
|
+
Identity: pass `agent_id` explicitly, or set `AGENT_MAIL_ID` once per agent.
|
|
238
|
+
|
|
239
|
+
## Optional: desktop notifications for humans
|
|
240
|
+
|
|
241
|
+
A companion watcher prints every new message as a JSON line and fires desktop notifications (macOS / Linux / Windows). It is never on the agent wake-up path — agents don't need it:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
uvx --from git+https://github.com/polaris-smart/agent-mailbox agent-mailbox-watch --notify boss
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Run it as a service on your platform:
|
|
248
|
+
|
|
249
|
+
| Platform | Install | Verify |
|
|
250
|
+
|----------|---------|--------|
|
|
251
|
+
| macOS (launchd) | `scripts/install-watch-macos.sh --notify boss` | `tail -f ~/.agent-mail/watch.log` |
|
|
252
|
+
| Linux (systemd user) | `scripts/install-watch-linux.sh …` | `journalctl --user -u agent-mailbox-watch -f` |
|
|
253
|
+
| Windows (schtasks) | `scripts\install-watch-windows.ps1` | `schtasks /Query /TN AgentMailboxWatch /V` |
|
|
254
|
+
|
|
255
|
+
## Maintenance: cleaning up test residue (cleanup)
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
python -m agent_mailbox.cleanup --dry-run # list only (the default behaviour)
|
|
259
|
+
python -m agent_mailbox.cleanup --dry-run --root ~/.agent-mail
|
|
260
|
+
python -m agent_mailbox.cleanup --yes # actually delete: explicit --yes + interactive confirmation
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Scans the mail root and lists **suspected test residue**: agent `inbox/` / `archive/` directories missing from registry.json, `NEWBIE` / `WBTEST`-style test-named directories, and orphan letters (stray files directly under `inbox/` / `archive/`, unparseable JSON, `*.tmp` left by an interrupted atomic write). Each finding prints path + size + reason; `--dry-run` (and the flagless default) deletes nothing; `--yes` deletes for real but requires typing `yes` to confirm. Registered-but-test-named directories are reported as review-only and never deleted. The `--yes` capability shipped with v0.4.0 — actually running it against a production mail root remains an explicit operator (boss) approval, separate from the release.
|
|
264
|
+
|
|
265
|
+
## Design
|
|
266
|
+
|
|
267
|
+
- **Local-first** — plain JSON files under `~/.agent-mail/`. No SMTP, no IMAP, no domain, no cloud relay, no network by default.
|
|
268
|
+
- **Register-once addressing** — `mailbox_register("HS")` is all it takes; every registered agent is immediately addressable by everyone.
|
|
269
|
+
- **Zero external dependencies** — only `mcp`. The store is one Python file with `flock`-guarded atomic writes; multiple MCP host processes share one mail root safely.
|
|
270
|
+
- **Human-readable** — every message is a small JSON file you can `cat`. The boss reads the inbox directly.
|
|
271
|
+
- **Honors existing identities** — set `AGENT_MAIL_ID` in each agent's environment and its tools become self-addressed.
|
|
272
|
+
|
|
273
|
+
## Security notes
|
|
274
|
+
|
|
275
|
+
- Mail root lives in your home directory; messages never leave the machine unless you opt into the webhook, which is pinned to loopback/private targets by default.
|
|
276
|
+
- Agent ids are strictly validated (`[A-Za-z0-9_-]`, ≤64 chars) — no path traversal.
|
|
277
|
+
- The store is append-oriented with atomic writes and file locks; a crashed writer cannot corrupt the registry.
|
|
278
|
+
- Webhook payloads are HMAC-signed; verifiers should compare with a constant-time function.
|
|
279
|
+
- For tamper-evidence, signed receipts (ed25519) are on the roadmap.
|
|
280
|
+
|
|
281
|
+
## Development
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
git clone https://github.com/polaris-smart/agent-mailbox && cd agent-mailbox
|
|
285
|
+
uv venv && uv pip install -e ".[dev]"
|
|
286
|
+
pytest
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Upgrading
|
|
290
|
+
|
|
291
|
+
Upgrade with `uv tool upgrade agent-mailbox` (or re-pull however you installed it).
|
|
292
|
+
|
|
293
|
+
⚠️ **Restart your agent session (or reconnect the MCP client) after upgrading** — MCP tool lists are enumerated at session start, so new tools (12 now, was 9) only appear after a restart. No config changes needed; `tasks.json` is created automatically on first use.
|
|
294
|
+
|
|
295
|
+
## Roadmap
|
|
296
|
+
|
|
297
|
+
- **v0.5.0** (current) — lifecycle hardening from the 2026-09-13 incidents (task `t-6`): **duplicate suppression** (delivery-side `semantic_hash`, same-hash non-terminal repeats within a 24h window return `{"deduped": true, "existing_id"}` with zero side effects; `dedupe: false` exempts; code-fence-raw hashing, inbox-only scope, hash→inbox index); **half-done compensation** (`record_handled` two-phase intent/outcome API as the single `handled_log` writer + `resume_plan` four-row table: process / replay / finalize / skip); **stale-`acked` reclaim** shipped earlier as `reap_stale_acked` / `python -m agent_mailbox.reap` now wired into the wake loop (reap first, count second, fail-open) with the 铁1 coupling enforced — `reap_ttl` (3600s) must stay strictly below `dedup_ttl` (24h), violations fail loudly; **wake circuit breaker** — N consecutive no-progress drain rounds latch a breaker file and stop launching turns (backoff stretches the interval, the breaker stops the bleeding).
|
|
298
|
+
- **v0.5.x (open)** — still tracked from the t-6 reviews, not in this release: `status filtering` (ask for "pending or acked" views), `identity binding` (bind MCP callers to `AGENT_MAIL_ID` against foreign checks; today's model is local trust — anyone on the machine can read any box), `wake routing` (gateway subscription `to`-filter; lives outside this repo), `unread_count` in webhook payloads, claim semantics for `mailbox_wait` (P1–P4 from the 09-13 forensics).
|
|
299
|
+
- **v0.4.0** — feature batch: configurable webhook signature style (`AGENT_MAIL_SIGNATURE_STYLE`: github default / generic / slack); self-echo protection (notifications where sender == target are dropped by default, audited as `echo_suppressed` in `sent.log`; `notify_self_echo` / `AGENT_MAIL_NOTIFY_SELF_ECHO` restores delivery with an `[echo] ` subject prefix on the notification while the letter keeps its subject); new `cleanup --dry-run` maintenance command (scans for test residue, lists without deleting, `--yes` deletes after confirmation).
|
|
300
|
+
- **v0.3.1** — patch batch: reply subjects no longer pile up `Re: Re:` (first reply, re-replies, and mixed-case prefixes all normalize to a single `Re:`); web board tokens use constant-time comparison (`hmac.compare_digest`) and persist across reboots (`~/.agent-mail/web_token`, mode 0600, `AGENT_MAIL_WEB_TOKEN` env always wins); `sent.log` auto-rotates one generation past 10 MB (to `sent.log.1`).
|
|
301
|
+
- **v0.3.0** — task board + web kanban: `task_create` / `task_move` / `task_list` with a strict todo→doing→review→done state machine; creating or moving a card auto-messages the assignee, so board motion wakes agents with zero polling. `--web 8643` serves a token-protected zero-dependency kanban UI where human drag-and-drop goes through the same wake-up path. Messages + tasks + wake-up + board, still zero dependencies.
|
|
302
|
+
- **v0.5.0+** — maybe: deeper kanban integrations (Kaneo as reference/competitor). Under discussion.
|
|
303
|
+
- **Next** — federation: streamable HTTP transport for agents on other machines (Tailscale/LAN friendly); signed receipts (ed25519) for tamper-evident delivery.
|
|
304
|
+
- **v1.0.0** — cross-organization bridge: local threads reach agents on other machines and organizations over standard email infrastructure, with the same mailbox lifecycle.
|
|
305
|
+
|
|
306
|
+
## License
|
|
307
|
+
|
|
308
|
+
MIT
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# agent-mailbox
|
|
2
|
+
|
|
3
|
+
[](https://glama.ai/mcp/servers/polaris-smart/agent-mailbox)
|
|
4
|
+
|
|
5
|
+
**Un buzón propio para cada agente de IA local.** Un servidor MCP por stdio. Cero demonios. Un archivo JSON por mensaje. Más un tablero de tareas integrado: las tarjetas despiertan a su responsable al moverse, y un kanban web sin dependencias para el humano.
|
|
6
|
+
|
|
7
|
+
📖 **Docs**: [English](README.md) · [中文](README.zh-CN.md) · [Español](README.es.md) · [Português](README.pt-BR.md) · [Français](README.fr.md) · [Русский](README.ru.md)
|
|
8
|
+
|
|
9
|
+
> 🆕 **v0.3.0 — Tablero de tareas**: los agentes comparten ahora una superficie de tareas sobre la misma raíz de correo. 3 herramientas MCP nuevas (12 en total), un tablero de arrastrar y soltar sin dependencias (`--web`), y cada movimiento avisa al responsable. ⚠️ **Nota de actualización**: reinicia tu sesión de agente para cargar las herramientas nuevas. → [Tablero de tareas](#tablero-de-tareas)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## El problema
|
|
14
|
+
|
|
15
|
+
Ejecutar varios agentes de IA en una misma máquina — Claude Code, Hermes, tus propios scripts — y no tienen forma de dejarse mensajes. Se quedan esperándose, o terminas copiando y pegando entre ventanas como un operador humano.
|
|
16
|
+
|
|
17
|
+
## La solución
|
|
18
|
+
|
|
19
|
+
Un buzón es un directorio de archivos JSON simples:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
~/.agent-mail/
|
|
23
|
+
registry.json agent_id → {owner, description, created_at}
|
|
24
|
+
inbox/HS/20260905-….json un archivo por mensaje
|
|
25
|
+
archive/HS/…
|
|
26
|
+
tasks.json el tablero de tareas ({"next_id", "tasks": {id: tarjeta}})
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Los agentes lo leen y escriben a través de un pequeño servidor MCP por stdio. Sin proceso intermediario, sin puertos, sin base de datos, sin red por defecto. Cualquier número de hosts MCP comparten una misma raíz de correo de forma segura (con bloqueo de archivos).
|
|
30
|
+
|
|
31
|
+
## Inicio rápido
|
|
32
|
+
|
|
33
|
+
**Requisitos previos** — solo una vez: instala [uv](https://docs.astral.sh/uv/) (`curl -LsSf https://astral.sh/uv/install.sh | sh` en macOS/Linux, o `powershell -c "irm https://astral.sh/uv/install.ps1 | iex"` en Windows). `uvx` ejecuta todo lo demás; no hay nada más que instalar.
|
|
34
|
+
|
|
35
|
+
### 1 · Registra el servidor en tu host MCP
|
|
36
|
+
|
|
37
|
+
Claude Code:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
claude mcp add agent-mailbox -- uvx --from git+https://github.com/polaris-smart/agent-mailbox agent-mailbox
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Cualquier host MCP (JSON genérico):
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"mcpServers": {
|
|
48
|
+
"agent-mailbox": {
|
|
49
|
+
"command": "uvx",
|
|
50
|
+
"args": ["--from", "git+https://github.com/polaris-smart/agent-mailbox", "agent-mailbox"]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Consejo: define `AGENT_MAIL_ID=HS` (o el id que quieras) en el entorno del agente y todas las herramientas quedan autodireccionadas — sin pasar `agent_id` en cada llamada.
|
|
57
|
+
|
|
58
|
+
### 2 · Los agentes se registran una vez
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{ "tool": "mailbox_register", "arguments": { "agent_id": "HS", "owner": "Hermes", "description": "PM & QA" } }
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
El registro es idempotente. Todo agente registrado es direccionable de inmediato por todos — incluido un id humano `boss` que puedes leer tú mismo.
|
|
65
|
+
|
|
66
|
+
### 3 · Enviar, revisar, responder
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{ "tool": "mailbox_send", "arguments": { "to": "HS", "subject": "deploy ready", "body": "v0.1.0 preparada, por favor verifica." } }
|
|
70
|
+
{ "tool": "mailbox_check", "arguments": {} }
|
|
71
|
+
{ "tool": "mailbox_reply", "arguments": { "msg_id": "20260905-…-hs", "body": "verificado, marcado done." } }
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`mailbox_check` trae los mensajes pendientes y los marca `acked`. Ciclo de vida: `pending → acked → done`, y luego se archivan opcionalmente. Cada mensaje es un JSON que puedes `cat` — el jefe lee la bandeja directamente.
|
|
75
|
+
|
|
76
|
+
### 4 · Esperar en vez de sondear
|
|
77
|
+
|
|
78
|
+
`mailbox_wait` se bloquea (long-poll) hasta que llega un mensaje — llámalo como última acción del turno:
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{ "tool": "mailbox_wait", "arguments": { "timeout_seconds": 25 } }
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Tablero de tareas
|
|
85
|
+
|
|
86
|
+
Las tarjetas viven en `<raíz de correo>/tasks.json` (JSON plano, el mismo bloqueo de archivos que el correo). La máquina de estados es estricta: `todo→doing→review→done`; los saltos no adyacentes se rechazan salvo `force=True`, y `done` es terminal. Crear o mover una tarjeta envía al responsable un mensaje normal del buzón (`[task#t-12 → review] …`) — el movimiento del tablero despierta al agente por la bandeja existente, sin sondeos ni webhooks. Los movimientos que uno se hace a sí mismo permanecen en silencio, y `notify=False` los desactiva.
|
|
87
|
+
|
|
88
|
+
**Tablero web (para el humano).** `agent-mailbox --web 8643` sirve un kanban sin dependencias (`http.server` de stdlib + una sola página HTML embebida, sin frameworks) en `127.0.0.1`. Cuatro columnas reflejan la máquina de estados; arrastra una tarjeta entre columnas adyacentes para moverla, o crea tarjetas desde el formulario, con una sola alternancia entre tema claro y oscuro. La autenticación es un token bearer — define `AGENT_MAIL_WEB_TOKEN` para uno fijo, o se genera e imprime uno nuevo en cada arranque (abre `http://127.0.0.1:8643/?token=…`). El tablero actúa como agente `boss`: cada tarjeta que crees o arrastres sigue avisando al responsable — cada movimiento le envía un mensaje. La página se refresca cada 5 segundos.
|
|
89
|
+
|
|
90
|
+
## Despertar a un agente dormido (una línea de configuración)
|
|
91
|
+
|
|
92
|
+
Si el agente receptor ni siquiera está en ejecución, `mailbox_send` puede hacer POST de cada mensaje nuevo a un webhook en el instante en que aterriza — sin demonios, sin sondeo, sin procesos extra:
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
// ~/.agent-mail/webhook.json (chmod 600)
|
|
96
|
+
{ "url": "http://localhost:8644/webhooks/agent-mailbox", "secret": "…" }
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Genera el secreto una vez: `openssl rand -hex 32`. Omítelo para POSTs sin firmar (suficiente para pruebas locales; exigir la verificación lo decide el receptor).
|
|
100
|
+
|
|
101
|
+
El manejador de webhooks del host recibe:
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{ "event": "agent_mailbox_new_message", "event_type": "agent_mailbox_new_message", "message": { "id": "…", "from": "ZC", "to": "HS", "subject": "…", "body": "…" } }
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
…despierta al agente, y el agente llama a `mailbox_check` al llegar. Esa es toda la integración.
|
|
108
|
+
|
|
109
|
+
- Firmado con `X-Hub-Signature-256: sha256=<hmac>` (esquema GitHub — aceptado por Hermes gateway y la mayoría de consumidores de webhooks).
|
|
110
|
+
- El destino queda fijado: solo http/https, direcciones loopback/privadas por defecto, redirecciones rechazadas, proxy del sistema omitido.
|
|
111
|
+
- Las variables de entorno `AGENT_MAIL_WEBHOOK_URL` / `AGENT_MAIL_WEBHOOK_SECRET` tienen prioridad sobre el archivo. Sin configurar → totalmente offline.
|
|
112
|
+
|
|
113
|
+
## Las herramientas
|
|
114
|
+
|
|
115
|
+
| Herramienta | Notas |
|
|
116
|
+
|-------------|-------|
|
|
117
|
+
| `mailbox_register(agent_id, owner?, description?)` | reclama un buzón; idempotente |
|
|
118
|
+
| `mailbox_send(to, subject, body, priority?)` | `to` = un id, una lista, o `"all"` |
|
|
119
|
+
| `mailbox_check(agent_id?, mark?)` | trae pendientes (→ `acked`) |
|
|
120
|
+
| `mailbox_reply(msg_id, body)` | enruta de vuelta al remitente original |
|
|
121
|
+
| `mailbox_list(agent_id?, status?)` | lista mensajes, filtro opcional por estado |
|
|
122
|
+
| `mailbox_done(msg_id)` | marca como atendido |
|
|
123
|
+
| `mailbox_broadcast(subject, body)` | a todos los agentes registrados |
|
|
124
|
+
| `mailbox_whoami()` | directorio de agentes + raíz de correo |
|
|
125
|
+
| `mailbox_wait(agent_id?, timeout_seconds?)` | long-poll de correo nuevo |
|
|
126
|
+
| `task_create(title, assignee, due?)` | crea una tarjeta de tarea (arranca en `todo`); avisa al responsable |
|
|
127
|
+
| `task_move(task_id, status, assignee?, note?, force?)` | avanza por `todo→doing→review→done` (los saltos requieren `force`); mover una tarjeta avisa a su responsable |
|
|
128
|
+
| `task_list(assignee?, status?)` | lista tarjetas de tarea, filtros opcionales |
|
|
129
|
+
|
|
130
|
+
## Opcional: notificaciones de escritorio para humanos
|
|
131
|
+
|
|
132
|
+
Un watcher complementario imprime cada mensaje nuevo como línea JSON y lanza notificaciones de escritorio (macOS / Linux / Windows). Nunca está en la ruta de despertar de agentes — los agentes no lo necesitan:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
uvx --from git+https://github.com/polaris-smart/agent-mailbox agent-mailbox-watch --notify boss
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
| Plataforma | Instalar | Verificar |
|
|
139
|
+
|------------|----------|-----------|
|
|
140
|
+
| macOS (launchd) | `scripts/install-watch-macos.sh --notify boss` | `tail -f ~/.agent-mail/watch.log` |
|
|
141
|
+
| Linux (systemd user) | `scripts/install-watch-linux.sh …` | `journalctl --user -u agent-mailbox-watch -f` |
|
|
142
|
+
| Windows (schtasks) | `scripts\install-watch-windows.ps1` | `schtasks /Query /TN AgentMailboxWatch /V` |
|
|
143
|
+
|
|
144
|
+
## Diseño
|
|
145
|
+
|
|
146
|
+
- **Local-first** — archivos JSON simples bajo `~/.agent-mail/`. Sin SMTP, sin IMAP, sin dominio, sin relé en la nube, sin red por defecto.
|
|
147
|
+
- **Direccionamiento con un registro** — `mailbox_register("HS")` es todo lo que hace falta; todo agente registrado es direccionable por todos.
|
|
148
|
+
- **Cero dependencias externas** — solo `mcp`. El almacén es un archivo Python con escrituras atómicas protegidas por `flock`.
|
|
149
|
+
- **Legible por humanos** — cada mensaje es un JSON pequeño que puedes `cat`. El jefe lee la bandeja directamente.
|
|
150
|
+
- **Respeta identidades existentes** — define `AGENT_MAIL_ID` en el entorno de cada agente y sus herramientas quedan autodireccionadas.
|
|
151
|
+
|
|
152
|
+
## Notas de seguridad
|
|
153
|
+
|
|
154
|
+
- La raíz de correo vive en tu directorio personal; los mensajes nunca salen de la máquina salvo que actives el webhook, fijado por defecto a destinos loopback/privados.
|
|
155
|
+
- Los ids de agente se validan estrictamente (`[A-Za-z0-9_-]`, ≤64 caracteres) — sin path traversal.
|
|
156
|
+
- El almacén es orientado a anexión con escrituras atómicas y bloqueos; un escritor caído no corrompe el registro.
|
|
157
|
+
- Los payloads del webhook van firmados con HMAC; los verificadores deben usar comparación en tiempo constante.
|
|
158
|
+
- Los recibos firmados (ed25519) están en el roadmap.
|
|
159
|
+
|
|
160
|
+
## Desarrollo
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
git clone https://github.com/polaris-smart/agent-mailbox && cd agent-mailbox
|
|
164
|
+
uv venv && uv pip install -e ".[dev]"
|
|
165
|
+
pytest
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Actualización
|
|
169
|
+
|
|
170
|
+
Actualiza con `uv tool upgrade agent-mailbox` (o vuelve a instalar según tu método original).
|
|
171
|
+
|
|
172
|
+
⚠️ **Tras actualizar, reinicia tu sesión de agente (o reconecta el cliente MCP)** — la lista de herramientas MCP se enumera al iniciar la sesión, así que las herramientas nuevas (12 ahora, antes 9) solo aparecen tras un reinicio. No hay que cambiar ninguna configuración; `tasks.json` se crea automáticamente al primer uso.
|
|
173
|
+
|
|
174
|
+
## Roadmap
|
|
175
|
+
|
|
176
|
+
- **v0.4.0** (actual) — lote de funcionalidades: estilo de firma de webhook configurable (`AGENT_MAIL_SIGNATURE_STYLE`: github por defecto / generic / slack); protección contra auto-eco (las notificaciones donde remitente == destinatario no se entregan por defecto y se auditan como `echo_suppressed` en `sent.log`; `notify_self_echo` / `AGENT_MAIL_NOTIFY_SELF_ECHO` restaura la entrega con un prefijo `[echo] ` en el asunto de la notificación mientras la carta conserva el suyo); nuevo comando de mantenimiento `cleanup --dry-run` (escanea residuos de prueba, lista sin borrar, `--yes` borra tras confirmación).
|
|
177
|
+
- **v0.3.1** — lote de parches: los asuntos de respuesta ya no acumulan `Re: Re:` (primera respuesta, re-respuestas y prefijos con mayúsculas/minúsculas mixtas se normalizan a un solo `Re:`); los tokens del tablero web usan comparación en tiempo constante (`hmac.compare_digest`) y persisten entre reinicios (`~/.agent-mail/web_token`, modo 0600, el env `AGENT_MAIL_WEB_TOKEN` siempre gana); `sent.log` rota automáticamente una generación pasados los 10 MB (a `sent.log.1`).
|
|
178
|
+
- **v0.3.0** — tablero de tareas + kanban web: `task_create` / `task_move` / `task_list` con una estricta máquina de estados todo→doing→review→done; crear o mover una tarjeta avisa automáticamente al responsable, así el movimiento del tablero despierta agentes sin ningún sondeo. `--web 8643` sirve una interfaz kanban sin dependencias protegida por token donde el arrastre humano pasa por la misma ruta de despertar. Mensajes + tareas + despertar + tablero, cero dependencias.
|
|
179
|
+
- **v0.5.0+** — quizá: integraciones kanban más profundas (Kaneo como referencia/competidor). En discusión.
|
|
180
|
+
- **Siguiente** — federación: transporte HTTP streamable para agentes en otras máquinas (amigable con Tailscale/LAN); recibos firmados (ed25519) para entrega a prueba de manipulación.
|
|
181
|
+
- **v1.0.0** — puente entre organizaciones: los hilos locales alcanzan agentes en otras máquinas y organizaciones sobre infraestructura de email estándar, con el mismo ciclo de vida del buzón.
|
|
182
|
+
|
|
183
|
+
## Licencia
|
|
184
|
+
|
|
185
|
+
MIT
|