hermes-rine 0.1.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.
@@ -0,0 +1,6 @@
1
+ # Gitignored — live-LLM-crew E2E secrets/config (NEVER commit).
2
+ # Together.ai OpenAI-compatible endpoint. Sourced into the rig env and passed through
3
+ # by NAME (-e TOGETHER_API_KEY ...), so the key never lands on a command line or in git.
4
+ TOGETHER_API_KEY=tgp_v1_zI_GAGQ1bIKcdzi_nVZqbHlAMN-o-WphfMoOBhgTQ6E
5
+ RINE_E2E_MODEL=meta-llama/Llama-3.3-70B-Instruct-Turbo
6
+ RINE_E2E_BASE_URL=https://api.together.xyz/v1
@@ -0,0 +1,12 @@
1
+ .e2e.env
2
+ __pycache__/
3
+ *.egg-info/
4
+ *.py[cod]
5
+ .pytest_cache/
6
+ .ruff_cache/
7
+ .mypy_cache/
8
+ .venv/
9
+ dist/
10
+ build/
11
+ *.sqlite
12
+ *.sqlite3
@@ -0,0 +1,58 @@
1
+ # Contained rig for the Hermes Agent plugin (hermes-rine).
2
+ #
3
+ # Nous Research Hermes Agent + its dependency tree install INSIDE the container ONLY
4
+ # (into a venv on a named volume; see ../compose.hermes.yml). NOTHING Hermes-related is
5
+ # ever installed on the host — same hard rule as compose.openclaw.yml / compose.crewai.yml.
6
+ #
7
+ # Hermes itself is cloned at BUILD time to /opt/hermes (pinned tag, reproducible) and
8
+ # installed EDITABLE into the named-volume venv on first run (entrypoint), alongside the
9
+ # bind-mounted ./rine-sdk and ./rine-hermes[dev] — so the rig consumes the UNRELEASED SDK
10
+ # fixes directly and the plugin under test is live-editable.
11
+ #
12
+ # Typical use (from repo root):
13
+ # docker compose -f compose.hermes.yml build
14
+ # docker compose -f compose.hermes.yml run --rm hermes hermes --version
15
+ # docker compose -f compose.hermes.yml run --rm hermes pytest -q
16
+ # docker compose -f compose.hermes.yml run --rm hermes ruff check src tests
17
+ # docker compose -f compose.hermes.yml run --rm hermes python e2e/e2e_hermes.py
18
+ # # reset the venv: docker volume rm rine-hermes_hermes_venv
19
+ FROM ubuntu:24.04
20
+
21
+ # Headless install prerequisites for a bare ubuntu:24.04 (Hermes' setup-hermes.sh is
22
+ # interactive — we replicate its non-interactive core instead). ripgrep = Hermes' fast
23
+ # search backend; git for the clone + any VCS deps; build-essential for native wheels.
24
+ RUN apt-get update \
25
+ && apt-get install -y --no-install-recommends \
26
+ ca-certificates curl git python3 python3-venv python3-dev build-essential ripgrep jq \
27
+ && rm -rf /var/lib/apt/lists/*
28
+
29
+ # uv: fast resolver + Python toolchain manager (installs the pinned 3.11 interpreter).
30
+ RUN curl -LsSf https://astral.sh/uv/install.sh | sh
31
+ ENV PATH="/root/.local/bin:${PATH}"
32
+ RUN uv python install 3.11
33
+
34
+ # Hermes source, pinned to an exact commit (the repo tags by CalVer — v2026.6.x — while
35
+ # pyproject reports the internal version 0.16.0; this commit is the v0.16.0/main tip the
36
+ # plugin was built + verified against). Fetch-by-SHA (GitHub allows it) keeps the image
37
+ # reproducible and identical to the reference source the build read. Editable install into
38
+ # the named-volume venv happens in the entrypoint (first run).
39
+ ARG HERMES_REF=d62979a6f34f64f2ed840f159aac66e24d7cad78
40
+ RUN git init /opt/hermes \
41
+ && git -C /opt/hermes remote add origin https://github.com/NousResearch/hermes-agent \
42
+ && git -C /opt/hermes fetch --depth 1 origin "${HERMES_REF}" \
43
+ && git -C /opt/hermes checkout --detach FETCH_HEAD
44
+
45
+ ENV VENV=/opt/venv \
46
+ PATH="/opt/venv/bin:/root/.local/bin:${PATH}" \
47
+ PIP_DISABLE_PIP_VERSION_CHECK=1 \
48
+ PYTHONDONTWRITEBYTECODE=1 \
49
+ HERMES_HOME=/root/.hermes \
50
+ CI=true
51
+
52
+ WORKDIR /work/rine-hermes
53
+
54
+ COPY entrypoint.sh /usr/local/bin/rig-entrypoint
55
+ RUN chmod +x /usr/local/bin/rig-entrypoint
56
+
57
+ ENTRYPOINT ["rig-entrypoint"]
58
+ CMD ["python", "-c", "import hermes_rine, rine; print('hermes_rine', hermes_rine.__version__, '/ rine', rine.__version__)"]
@@ -0,0 +1,132 @@
1
+ Metadata-Version: 2.4
2
+ Name: hermes-rine
3
+ Version: 0.1.0
4
+ Summary: Official Hermes Agent plugin for the Rine network — 12 rine_* tools, an inbound wake channel, and a bundled skill for E2E-encrypted agent-to-agent messaging and groups
5
+ Project-URL: Homepage, https://rine.network
6
+ Project-URL: Documentation, https://docs.rine.network
7
+ Project-URL: Repository, https://codeberg.org/rine/rine-hermes
8
+ Project-URL: Issues, https://codeberg.org/rine/rine-hermes/issues
9
+ Author-email: mmmbs <mmmbs@proton.me>
10
+ License-Expression: EUPL-1.2
11
+ Keywords: agents,ai-agents,e2ee,hermes,hermes-agent,messaging,rine,tools
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
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
19
+ Classifier: Topic :: Security :: Cryptography
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: <3.14,>=3.11
22
+ Requires-Dist: pydantic>=2.0
23
+ Requires-Dist: rine>=0.2.2
24
+ Provides-Extra: dev
25
+ Requires-Dist: aiosqlite>=0.20; extra == 'dev'
26
+ Requires-Dist: mypy>=1.13; extra == 'dev'
27
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
28
+ Requires-Dist: pytest>=8.0; extra == 'dev'
29
+ Requires-Dist: pyyaml>=6.0; extra == 'dev'
30
+ Requires-Dist: respx>=0.22; extra == 'dev'
31
+ Requires-Dist: ruff>=0.8; extra == 'dev'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # hermes-rine
35
+
36
+ Official [Hermes Agent](https://github.com/NousResearch/hermes-agent) plugin for the
37
+ [rine.network](https://rine.network) agent-to-agent network. It gives a Hermes agent
38
+ twelve `rine_*` tools, a bundled `rine:rine` skill, and an inbound wake channel — so the
39
+ agent can send, receive, discover, and reply to **end-to-end-encrypted** messages with
40
+ other AI agents, and wake automatically when new mail arrives.
41
+
42
+ All cryptography, transport, and credential resolution come from the
43
+ [`rine`](https://pypi.org/project/rine/) Python SDK; this plugin never reimplements
44
+ them, and ciphertext never enters the model's context.
45
+
46
+ ## Install
47
+
48
+ ```sh
49
+ pip install hermes-rine # primary path — installs the SDK and registers the entry point
50
+ python -m hermes_rine.onboard \ # one-time: register an org + create an agent (~30-60s PoW)
51
+ --email you@example.com --org-slug myorg --org-name "My Org" --agent-name assistant
52
+ python -m hermes_rine.enable # enable the plugin (adds it to config.yaml — see below)
53
+ hermes gateway run # or just `hermes` for an interactive agent
54
+ ```
55
+
56
+ **Enabling the plugin.** `hermes plugins enable rine` does **not** work for a
57
+ pip *entry-point* plugin on Hermes v0.16.0 — `hermes plugins` only scans bundled and
58
+ directory plugins, so it reports `rine` as "not installed or bundled". The supported
59
+ activation path is config-based: add `rine` to `plugins.enabled` in `~/.hermes/config.yaml`.
60
+ `python -m hermes_rine.enable` does this idempotently for you; equivalently, edit the file
61
+ by hand:
62
+
63
+ ```yaml
64
+ plugins:
65
+ enabled:
66
+ - rine
67
+ ```
68
+
69
+ Hermes plugins are also git-installable, but `hermes plugins install` does **not** run
70
+ `pip install` — so a git-installed copy that `import rine`s would fail. Always install
71
+ via `pip install hermes-rine` (entry points). Already have credentials? Skip onboarding
72
+ and set `RINE_CLIENT_ID` / `RINE_CLIENT_SECRET` (or point `RINE_CONFIG_DIR` at a config
73
+ directory that holds `credentials.json`).
74
+
75
+ ## Tools
76
+
77
+ `rine_send`, `rine_send_and_wait`, `rine_check_inbox`, `rine_read`, `rine_reply`,
78
+ `rine_discover`, `rine_inspect`, `rine_whoami`, `rine_group_create`, `rine_group_invite`,
79
+ `rine_group_remove`, `rine_group_inspect`.
80
+
81
+ The whole toolset is hidden until credentials resolve. Mutating tools run unattended by
82
+ default; set `RINE_REQUIRE_CONFIRM=1` to require operator confirmation before any
83
+ irreversible send/group action.
84
+
85
+ ## Waking on inbound mail
86
+
87
+ Run the gateway and the agent wakes transparently on each new message. The rine platform
88
+ activates automatically once credentials resolve (no extra config block needed):
89
+
90
+ ```sh
91
+ GATEWAY_ALLOW_ALL_USERS=true hermes gateway run
92
+ ```
93
+
94
+ `GATEWAY_ALLOW_ALL_USERS=true` is required: Hermes' gateway denies senders by default
95
+ (it has no rine-specific allowlist), so without it inbound A2A messages are dropped. rine
96
+ already authenticates every sender at the network layer, and the plugin verifies message
97
+ signatures — set `RINE_REQUIRE_VERIFIED=1` to also drop messages whose signature can't be
98
+ verified, and `RINE_ALLOWED_HANDLES` to restrict which peers may wake you.
99
+
100
+ Each inbound message starts a turn with the `rine:rine` skill loaded and routes your
101
+ reply back out — exactly once, even across a gateway restart. Tune the poll cadence with
102
+ `RINE_POLL_INTERVAL` (seconds, default 30) or set `RINE_TRANSPORT=sse` for a push stream.
103
+
104
+ **Cron fallback (no gateway).** In a one-shot or interactive setup nothing pushes mail
105
+ to you. Schedule a recurring job that checks your poll URL and starts a triage turn when
106
+ the undelivered count is non-zero, or just call `rine_check_inbox` at the start of any
107
+ active turn. See the skill's `references/hermes.md` for a sketch.
108
+
109
+ ## MCP alternative
110
+
111
+ Prefer not to install a plugin? rine also ships an MCP server
112
+ (`@rine-network/mcp`). Point any MCP-capable Hermes setup at it for the same send/read/
113
+ discover surface, without the bundled skill or the gateway wake channel.
114
+
115
+ ## Troubleshooting
116
+
117
+ - **Tools don't appear** — credentials aren't resolving. Confirm with
118
+ `python -m hermes_rine.onboard`, or set `RINE_CLIENT_ID`/`RINE_CLIENT_SECRET`, then
119
+ re-list tools. The toolset stays hidden until creds are present.
120
+ - **"Rine auth failed"** — same cause; onboard or set the env vars.
121
+ - **A message shows `[unreadable]`** — it uses MLS or PQ-hybrid encryption, which the
122
+ Python side can't decrypt. Read it with the rine CLI / MCP / TypeScript SDK, or have
123
+ the sender use a sender-key group.
124
+ - **`hermes plugins enable rine` says "not installed or bundled"** — expected for a pip
125
+ entry-point plugin; `hermes plugins` only scans directory plugins. Enable it via config
126
+ instead: `python -m hermes_rine.enable` (adds `rine` to `plugins.enabled`).
127
+ - **`hermes plugins list` shows rine but it won't load** — you git-installed it; install
128
+ with `pip install hermes-rine` so the SDK is present.
129
+
130
+ ## License
131
+
132
+ EUPL-1.2.
@@ -0,0 +1,99 @@
1
+ # hermes-rine
2
+
3
+ Official [Hermes Agent](https://github.com/NousResearch/hermes-agent) plugin for the
4
+ [rine.network](https://rine.network) agent-to-agent network. It gives a Hermes agent
5
+ twelve `rine_*` tools, a bundled `rine:rine` skill, and an inbound wake channel — so the
6
+ agent can send, receive, discover, and reply to **end-to-end-encrypted** messages with
7
+ other AI agents, and wake automatically when new mail arrives.
8
+
9
+ All cryptography, transport, and credential resolution come from the
10
+ [`rine`](https://pypi.org/project/rine/) Python SDK; this plugin never reimplements
11
+ them, and ciphertext never enters the model's context.
12
+
13
+ ## Install
14
+
15
+ ```sh
16
+ pip install hermes-rine # primary path — installs the SDK and registers the entry point
17
+ python -m hermes_rine.onboard \ # one-time: register an org + create an agent (~30-60s PoW)
18
+ --email you@example.com --org-slug myorg --org-name "My Org" --agent-name assistant
19
+ python -m hermes_rine.enable # enable the plugin (adds it to config.yaml — see below)
20
+ hermes gateway run # or just `hermes` for an interactive agent
21
+ ```
22
+
23
+ **Enabling the plugin.** `hermes plugins enable rine` does **not** work for a
24
+ pip *entry-point* plugin on Hermes v0.16.0 — `hermes plugins` only scans bundled and
25
+ directory plugins, so it reports `rine` as "not installed or bundled". The supported
26
+ activation path is config-based: add `rine` to `plugins.enabled` in `~/.hermes/config.yaml`.
27
+ `python -m hermes_rine.enable` does this idempotently for you; equivalently, edit the file
28
+ by hand:
29
+
30
+ ```yaml
31
+ plugins:
32
+ enabled:
33
+ - rine
34
+ ```
35
+
36
+ Hermes plugins are also git-installable, but `hermes plugins install` does **not** run
37
+ `pip install` — so a git-installed copy that `import rine`s would fail. Always install
38
+ via `pip install hermes-rine` (entry points). Already have credentials? Skip onboarding
39
+ and set `RINE_CLIENT_ID` / `RINE_CLIENT_SECRET` (or point `RINE_CONFIG_DIR` at a config
40
+ directory that holds `credentials.json`).
41
+
42
+ ## Tools
43
+
44
+ `rine_send`, `rine_send_and_wait`, `rine_check_inbox`, `rine_read`, `rine_reply`,
45
+ `rine_discover`, `rine_inspect`, `rine_whoami`, `rine_group_create`, `rine_group_invite`,
46
+ `rine_group_remove`, `rine_group_inspect`.
47
+
48
+ The whole toolset is hidden until credentials resolve. Mutating tools run unattended by
49
+ default; set `RINE_REQUIRE_CONFIRM=1` to require operator confirmation before any
50
+ irreversible send/group action.
51
+
52
+ ## Waking on inbound mail
53
+
54
+ Run the gateway and the agent wakes transparently on each new message. The rine platform
55
+ activates automatically once credentials resolve (no extra config block needed):
56
+
57
+ ```sh
58
+ GATEWAY_ALLOW_ALL_USERS=true hermes gateway run
59
+ ```
60
+
61
+ `GATEWAY_ALLOW_ALL_USERS=true` is required: Hermes' gateway denies senders by default
62
+ (it has no rine-specific allowlist), so without it inbound A2A messages are dropped. rine
63
+ already authenticates every sender at the network layer, and the plugin verifies message
64
+ signatures — set `RINE_REQUIRE_VERIFIED=1` to also drop messages whose signature can't be
65
+ verified, and `RINE_ALLOWED_HANDLES` to restrict which peers may wake you.
66
+
67
+ Each inbound message starts a turn with the `rine:rine` skill loaded and routes your
68
+ reply back out — exactly once, even across a gateway restart. Tune the poll cadence with
69
+ `RINE_POLL_INTERVAL` (seconds, default 30) or set `RINE_TRANSPORT=sse` for a push stream.
70
+
71
+ **Cron fallback (no gateway).** In a one-shot or interactive setup nothing pushes mail
72
+ to you. Schedule a recurring job that checks your poll URL and starts a triage turn when
73
+ the undelivered count is non-zero, or just call `rine_check_inbox` at the start of any
74
+ active turn. See the skill's `references/hermes.md` for a sketch.
75
+
76
+ ## MCP alternative
77
+
78
+ Prefer not to install a plugin? rine also ships an MCP server
79
+ (`@rine-network/mcp`). Point any MCP-capable Hermes setup at it for the same send/read/
80
+ discover surface, without the bundled skill or the gateway wake channel.
81
+
82
+ ## Troubleshooting
83
+
84
+ - **Tools don't appear** — credentials aren't resolving. Confirm with
85
+ `python -m hermes_rine.onboard`, or set `RINE_CLIENT_ID`/`RINE_CLIENT_SECRET`, then
86
+ re-list tools. The toolset stays hidden until creds are present.
87
+ - **"Rine auth failed"** — same cause; onboard or set the env vars.
88
+ - **A message shows `[unreadable]`** — it uses MLS or PQ-hybrid encryption, which the
89
+ Python side can't decrypt. Read it with the rine CLI / MCP / TypeScript SDK, or have
90
+ the sender use a sender-key group.
91
+ - **`hermes plugins enable rine` says "not installed or bundled"** — expected for a pip
92
+ entry-point plugin; `hermes plugins` only scans directory plugins. Enable it via config
93
+ instead: `python -m hermes_rine.enable` (adds `rine` to `plugins.enabled`).
94
+ - **`hermes plugins list` shows rine but it won't load** — you git-installed it; install
95
+ with `pip install hermes-rine` so the SDK is present.
96
+
97
+ ## License
98
+
99
+ EUPL-1.2.
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env bash
2
+ # Idempotent rig bootstrap for the contained hermes-rine test rig.
3
+ #
4
+ # Ensures the named-volume venv at $VENV holds:
5
+ # - Hermes Agent (editable, from /opt/hermes — makes `hermes` CLI + gateway/tools/
6
+ # hermes_cli/plugins importable)
7
+ # - the bind-mounted local rine-sdk (editable — unreleased fixes)
8
+ # - the bind-mounted hermes-rine[dev] (editable — the plugin under test + pytest/respx/...)
9
+ # Then writes ~/.hermes/{config.yaml,.env} (Together AI via the OpenAI-compatible custom
10
+ # provider) if absent, and exec's whatever command compose/`run` passed.
11
+ #
12
+ # The venv + Hermes' heavy dep tree live on a named volume, so the (slow) first install is
13
+ # cached for every later run and never touches the host.
14
+ set -euo pipefail
15
+
16
+ VENV="${VENV:-/opt/venv}"
17
+ MARKER="$VENV/.rig-installed"
18
+ HERMES_HOME="${HERMES_HOME:-/root/.hermes}"
19
+
20
+ if [ ! -f "$MARKER" ]; then
21
+ echo ">>> [rig] bootstrapping venv at $VENV (first run installs Hermes + deps; slow, 2-6 min)"
22
+ UV="$(command -v uv)"
23
+ "$UV" venv --clear --python 3.11 "$VENV"
24
+ # Force hermes-rine's `rine` dependency to resolve to the bind-mounted LOCAL SDK
25
+ # (unreleased fixes), overriding the published floor pinned in pyproject.toml.
26
+ echo "rine @ file:///work/rine-sdk" > /tmp/rig-overrides.txt
27
+ "$UV" pip install --python "$VENV/bin/python" \
28
+ --override /tmp/rig-overrides.txt \
29
+ -e /opt/hermes \
30
+ -e /work/rine-sdk \
31
+ -e "/work/rine-hermes[dev]"
32
+ touch "$MARKER"
33
+ echo ">>> [rig] bootstrap complete"
34
+ fi
35
+
36
+ # ---- Hermes config: Together AI via the OpenAI-compatible `custom` provider ----------
37
+ mkdir -p "$HERMES_HOME"
38
+ MODEL="${RINE_E2E_MODEL:-meta-llama/Llama-3.3-70B-Instruct-Turbo}"
39
+ BASE_URL="${RINE_E2E_BASE_URL:-https://api.together.xyz/v1}"
40
+
41
+ if [ ! -f "$HERMES_HOME/config.yaml" ]; then
42
+ echo ">>> [rig] writing $HERMES_HOME/config.yaml (model=$MODEL)"
43
+ cat > "$HERMES_HOME/config.yaml" <<YAML
44
+ # Generated by the hermes-rine rig (entrypoint.sh). Together AI, OpenAI-compatible.
45
+ model:
46
+ default: "$MODEL"
47
+ provider: "custom"
48
+ base_url: "$BASE_URL"
49
+ context_length: 131072 # >= MINIMUM_CONTEXT_LENGTH (64k); pinned so the gate is deterministic
50
+ # Surface the rine toolset in CLI sessions (the plugin registers toolset "rine").
51
+ platform_toolsets:
52
+ cli: [terminal, file, skills, todo, rine]
53
+ # Enable the rine plugin. NOTE: \`hermes plugins enable rine\` does NOT work for pip
54
+ # entry-point plugins on this Hermes version — its discovery (_discover_all_plugins) only
55
+ # scans bundled/user DIRECTORY plugins, so it reports "not installed or bundled". The
56
+ # loader (discover_and_load -> _scan_entry_points) DOES find pip plugins and gates them on
57
+ # this config key. So the correct enable path for a pip plugin is config-based:
58
+ plugins:
59
+ enabled: [rine]
60
+ YAML
61
+ fi
62
+
63
+ # Together key: Hermes derives the env var from the provider host (api.together.xyz ->
64
+ # TOGETHER_API_KEY). We persist it to ~/.hermes/.env (the documented mechanism) and keep
65
+ # it in the process env (compose env_file). OPENAI_API_KEY is set as a belt-and-braces
66
+ # fallback for the generic OpenAI-compatible client path.
67
+ if [ -n "${TOGETHER_API_KEY:-}" ] && [ ! -f "$HERMES_HOME/.env" ]; then
68
+ echo ">>> [rig] writing $HERMES_HOME/.env (TOGETHER_API_KEY)"
69
+ {
70
+ echo "TOGETHER_API_KEY=${TOGETHER_API_KEY}"
71
+ echo "OPENAI_API_KEY=${TOGETHER_API_KEY}"
72
+ } > "$HERMES_HOME/.env"
73
+ chmod 600 "$HERMES_HOME/.env"
74
+ fi
75
+
76
+ exec "$@"
@@ -0,0 +1,26 @@
1
+ # Hermes plugin manifest (directory-install path + `hermes plugins list` cosmetics).
2
+ # Primary distribution is `pip install hermes-rine` (entry points), not git-install.
3
+ name: rine
4
+ version: 0.1.0
5
+ description: >-
6
+ Send, receive, discover, and reply to messages with other AI agents over the
7
+ rine.network A2A network (E2E-encrypted). Wakes the agent on inbound mail.
8
+ author: mmmbs <mmmbs@proton.me>
9
+ kind: platform
10
+ manifest_version: 1
11
+ requires_env:
12
+ - RINE_CLIENT_ID
13
+ - RINE_CLIENT_SECRET
14
+ provides_tools:
15
+ - rine_send
16
+ - rine_send_and_wait
17
+ - rine_check_inbox
18
+ - rine_read
19
+ - rine_reply
20
+ - rine_discover
21
+ - rine_inspect
22
+ - rine_whoami
23
+ - rine_group_create
24
+ - rine_group_invite
25
+ - rine_group_remove
26
+ - rine_group_inspect
@@ -0,0 +1,127 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "hermes-rine"
7
+ version = "0.1.0"
8
+ description = "Official Hermes Agent plugin for the Rine network — 12 rine_* tools, an inbound wake channel, and a bundled skill for E2E-encrypted agent-to-agent messaging and groups"
9
+ readme = "README.md"
10
+ license = "EUPL-1.2"
11
+ requires-python = ">=3.11,<3.14"
12
+ authors = [{ name = "mmmbs", email = "mmmbs@proton.me" }]
13
+ keywords = ["rine", "hermes", "hermes-agent", "agents", "tools", "e2ee", "messaging", "ai-agents"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Topic :: Communications",
22
+ "Topic :: Security :: Cryptography",
23
+ "Typing :: Typed",
24
+ ]
25
+ dependencies = [
26
+ # The rine SDK owns all crypto/HTTP/config resolution; never vendored. Dev/CI
27
+ # inject the editable local ./rine-sdk over this floor (compose.hermes.yml).
28
+ # hermes-agent is NOT a runtime dependency: the rig supplies it on PYTHONPATH;
29
+ # `import hermes_rine` and `python -m hermes_rine.onboard` work without it.
30
+ "rine>=0.2.2",
31
+ "pydantic>=2.0",
32
+ ]
33
+
34
+ [project.urls]
35
+ Homepage = "https://rine.network"
36
+ Documentation = "https://docs.rine.network"
37
+ Repository = "https://codeberg.org/rine/rine-hermes"
38
+ Issues = "https://codeberg.org/rine/rine-hermes/issues"
39
+
40
+ [project.entry-points."hermes_agent.plugins"]
41
+ # Value is the MODULE (not module:attr). Hermes' loader (_load_entrypoint_module) does
42
+ # ep.load() then getattr(module, "register"): a "hermes_rine:register" value loads the
43
+ # FUNCTION, then looks for `.register` on it → "no register() function". The Hermes docs
44
+ # example is `my-plugin = "my_plugin_package"` — point at the package.
45
+ rine = "hermes_rine"
46
+
47
+ [project.optional-dependencies]
48
+ dev = [
49
+ "pytest>=8.0",
50
+ "pytest-asyncio>=0.24",
51
+ "respx>=0.22",
52
+ "ruff>=0.8",
53
+ "mypy>=1.13",
54
+ "aiosqlite>=0.20",
55
+ # The enable helper round-trips config.yaml; in the Hermes venv ruamel/pyyaml are
56
+ # always present (Hermes deps), but the standalone test env needs a YAML parser.
57
+ "pyyaml>=6.0",
58
+ ]
59
+
60
+ [tool.hatch.build.targets.sdist]
61
+ exclude = ["phases/", "e2e/", "tests/"]
62
+
63
+ [tool.hatch.build.targets.wheel]
64
+ packages = ["src/hermes_rine"]
65
+
66
+ [tool.pytest.ini_options]
67
+ testpaths = ["tests"]
68
+ pythonpath = ["src"]
69
+ asyncio_mode = "auto"
70
+
71
+ [tool.ruff]
72
+ target-version = "py311"
73
+ line-length = 99
74
+ src = ["src"]
75
+
76
+ [tool.ruff.lint]
77
+ select = [
78
+ "E", "F", "W", "I", "UP", "B", "SIM", "TCH",
79
+ "D101", "D102", "D103", "D107", "D205", "D400",
80
+ ]
81
+ ignore = ["D100"]
82
+
83
+ [tool.ruff.lint.per-file-ignores]
84
+ # Test names are self-documenting `test_<what>_<when>_<expected>`; per-symbol
85
+ # docstrings would be noise.
86
+ "tests/**" = ["D101", "D102", "D103", "D107"]
87
+ # The live E2E driver is a script (excluded from the sdist), not library code.
88
+ "e2e/**" = ["D101", "D102", "D103", "D107", "E501"]
89
+
90
+ [tool.ruff.lint.pydocstyle]
91
+ convention = "google"
92
+
93
+ [tool.mypy]
94
+ python_version = "3.11"
95
+ strict = true
96
+ warn_return_any = true
97
+ warn_unused_configs = true
98
+ packages = ["hermes_rine"]
99
+ mypy_path = "src"
100
+
101
+ # Hermes internals (``tools.registry``, ``gateway.*``) are NOT a typed runtime
102
+ # dependency — they are only present on PYTHONPATH inside a live Hermes process /
103
+ # the Docker rig. At our boundary their values are legitimately ``Any`` (and the base
104
+ # adapter is ``Any`` when Hermes is absent). Relaxing return-Any/subclass strictness on
105
+ # exactly the boundary modules keeps the gate green WITH or WITHOUT hermes-agent
106
+ # installed, without weakening checks on our own typed code (_journal/_format/_client/
107
+ # _gate/_schemas). ``disable_error_code`` (not per-line ignores) avoids the
108
+ # ``warn_unused_ignores`` trap when Hermes IS present.
109
+ [[tool.mypy.overrides]]
110
+ module = [
111
+ "hermes_rine.adapter",
112
+ "hermes_rine._poll",
113
+ "hermes_rine._sse",
114
+ "hermes_rine.tools.messaging",
115
+ "hermes_rine.tools.discovery",
116
+ "hermes_rine.tools.groups",
117
+ ]
118
+ warn_return_any = false
119
+ disable_error_code = ["no-any-return", "misc"]
120
+
121
+ # ``enable`` dynamically imports an optional YAML lib (ruamel/pyyaml) + ``hermes_cli`` —
122
+ # all absent / unstubbed off the Hermes venv, so their values are legitimately ``Any``
123
+ # and the import errors are environment-dependent. Disable just the import-resolution
124
+ # codes here (per-line ignores would be ``unused`` in the rig where the libs ARE present).
125
+ [[tool.mypy.overrides]]
126
+ module = ["hermes_rine.enable"]
127
+ disable_error_code = ["import-untyped", "import-not-found"]
@@ -0,0 +1,48 @@
1
+ """hermes-rine — official Hermes Agent plugin for the Rine network.
2
+
3
+ A single plugin that is simultaneously a **tool set** (12 ``rine_*`` tools), an
4
+ **inbound channel** (a gateway platform adapter that wakes the agent on new mail),
5
+ and a **bundled skill** (``rine:rine``). All crypto, HTTP, and config-dir resolution
6
+ come from the ``rine`` SDK; this package never reimplements them.
7
+
8
+ **Import discipline (critical).** This module imports *nothing heavy*. ``import
9
+ hermes_rine`` succeeds even without ``hermes-agent`` installed and without the SDK
10
+ touching the network. The Hermes-internal registrars (``tools.registry``,
11
+ ``gateway.platforms.base``, ``hermes_cli.plugins``) are imported **lazily inside
12
+ ``register()``**, which only ever runs inside a live Hermes process. Likewise no
13
+ client is constructed, no credential is read, and no listener is started at import
14
+ or ``register()`` time — that work is deferred to a tool's first handler call or the
15
+ adapter's ``connect()``.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from importlib.metadata import PackageNotFoundError, version
21
+ from typing import Any
22
+
23
+ try:
24
+ __version__ = version("hermes-rine")
25
+ except PackageNotFoundError: # not installed (e.g. running from a source checkout)
26
+ __version__ = "0.1.0"
27
+
28
+
29
+ def register(ctx: Any) -> None:
30
+ """Hermes plugin entry point — wire up tools, skill, and platform.
31
+
32
+ Called by Hermes with a ``PluginContext`` once the plugin is enabled. Imports
33
+ the registrars lazily so the heavy Hermes-internal modules are only pulled in
34
+ inside a Hermes process (never at ``import hermes_rine`` time).
35
+
36
+ Args:
37
+ ctx: The Hermes ``PluginContext`` (``hermes_cli.plugins.PluginContext``).
38
+ """
39
+ from hermes_rine.platform import register_rine_platform
40
+ from hermes_rine.skill_reg import register_rine_skill
41
+ from hermes_rine.tools import register_rine_tools
42
+
43
+ register_rine_tools(ctx)
44
+ register_rine_skill(ctx)
45
+ register_rine_platform(ctx)
46
+
47
+
48
+ __all__ = ["__version__", "register"]