Metadata-Version: 2.4
Name: shadownet-hermes-plugin
Version: 0.6.0
Summary: Shadownet protocol plugin for Hermes Agent.
Project-URL: Homepage, https://github.com/shadownet-protocol/shadownet/tree/main/integrations/plugins/hermes-agent
Project-URL: Repository, https://github.com/shadownet-protocol/shadownet
Project-URL: Specifications, https://github.com/shadownet-protocol/shadownet-specs
Project-URL: Issues, https://github.com/shadownet-protocol/shadownet/issues
Author: Shadownet contributors
License: MIT
Keywords: a2a,agents,hermes-agent,mcp,shadownet
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Communications
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.7
Requires-Dist: shadownet<0.7,>=0.6.0
Description-Content-Type: text/markdown

# Shadownet plugin for Hermes Agent

Identity-anchored agent-to-agent communication via the [Shadownet
protocol](https://github.com/shadownet-protocol/shadownet-specs), packaged
as a Hermes Agent plugin against every surface the
[Hermes plugin guide](https://hermes-agent.nousresearch.com/docs/guides/build-a-hermes-plugin)
documents.

## Architecture

```
┌──────────────────────────────────────────────────────────────────────┐
│  shadownet-hermes-plugin                                             │
│                                                                      │
│  ┌──────────────┐   ┌──────────────┐   ┌──────────────────────────┐  │
│  │  platform    │   │ slash + CLI  │   │  skills (4 bundled)      │  │
│  │  adapter     │   │ commands     │   │                          │  │
│  │  (inbound)   │   │  + hooks     │   │  ctx.register_skill +    │  │
│  └──────┬───────┘   └──────┬───────┘   │  legacy materialization  │  │
│         │                  │           └──────────┬───────────────┘  │
└─────────┼──────────────────┼──────────────────────┼──────────────────┘
          │                  │                      │
          ▼                  ▼                      ▼
      sidecar          Hermes gateway           ~/.hermes/skills/
      long-poll        (CLI + Telegram)         shadownet/<name>/
      social_inbox_wait

Outbound MCP tools (mcp_shadownet_*) live on a separate Hermes surface:
~/.hermes/config.yaml mcp_servers.shadownet — written automatically at
register() time.
```

Five concrete surfaces are wired by `register(ctx)`:

| Surface | Hermes API | What it gives the user |
| --- | --- | --- |
| Platform adapter | `ctx.register_platform(..., platform_hint=…, env_enablement_fn=…)` | Long-poll inbound from the sidecar; `social_send` mapping for outbound |
| MCP server | `~/.hermes/config.yaml` `mcp_servers.shadownet` (config-driven; the only canonical path per the guide) | Agent sees `mcp_shadownet_*` tools |
| Skills | `ctx.register_skill` (namespaced) + categorized materialization | Four skills are both opt-in via `shadownet:<name>` and surfaced in `<available_skills>` |
| Slash commands | `ctx.register_command` × 6 | `/shadownet-setup`, `/shadownet-inbox`, `/shadownet-reach-out`, `/shadownet-coordinate`, `/shadownet-status`, `/shadownet-logout` |
| Hooks | `ctx.register_hook` × 3 | `on_session_start` collects pending-inbox count; `pre_llm_call` injects on the first turn; `on_session_end` cleans up |
| CLI subcommands | `ctx.register_cli_command(name="shadownet", …)` | `hermes shadownet status|doctor|sync|logout` |

## Install

### Canonical path: `hermes plugins install` via the shim repo

Visit your sidecar's `/connect/hermes-agent` page (mints your bearer
token and hands you a paste-ready one-liner). Inside your Hermes
environment, paste:

```sh
echo 'SHADOWNET_CONNECT_URL=shadownet://connect?base=<sidecar>&token=<minted>' >> ~/.hermes/.env \
  && hermes plugins install shadownet-protocol/hermes-plugin --enable \
  && hermes gateway restart
```

The shim at
[`shadownet-protocol/hermes-plugin`](https://github.com/shadownet-protocol/hermes-plugin)
clones into `~/.hermes/plugins/shadownet/`, then bootstraps this PyPI
package into Hermes' venv on first `register()` (~10–30s, one-time).

### Direct pip install (lazy-install disabled)

```sh
pip install shadownet-hermes-plugin
echo 'SHADOWNET_CONNECT_URL=shadownet://connect?base=<sidecar>&token=<minted>' >> ~/.hermes/.env
hermes gateway restart
```

The package exposes `shadownet_hermes_plugin:register` as a
`hermes_agent.plugins` entry point — Hermes auto-discovers it on next
start without the shim repo.

## Slash commands

All six commands work in CLI sessions, the Telegram bot menu, Discord,
and anywhere else Hermes' gateway runs.

| Command | What it does |
| --- | --- |
| `/shadownet-setup` | Load the `shadownet:shadownet-setup` skill — verify identity + check connection |
| `/shadownet-inbox` | Load the inbox-triage skill |
| `/shadownet-reach-out` | Load the contact-message skill |
| `/shadownet-coordinate` | Load the two-sided coordination skill |
| `/shadownet-status` | Show connection state without leaving the chat (delegates to `hermes shadownet status`) |
| `/shadownet-logout` | Disconnect this Hermes from shadownet |

## CLI subcommands

The plugin registers a `hermes shadownet` subcommand tree for terminal
operators:

```sh
hermes shadownet status   # one-line state of each surface
hermes shadownet doctor   # OK/FAIL self-check per surface + overall verdict
hermes shadownet sync     # re-write MCP config + re-materialize skills (idempotent)
hermes shadownet logout   # disconnect: remove mcp_servers.shadownet, strip CONNECT_URL, disable platform
```

`logout` does **not** uninstall the plugin or revoke the upstream
token (the token is just orphaned locally). To uninstall, use
`hermes plugins remove shadownet-protocol/hermes-plugin` afterward.

## Hooks

| Hook | When | What it does |
| --- | --- | --- |
| `on_session_start` | New session on a non-shadownet platform | Cheap GET to `/v1/account/me/social/inbox`; if items > 0, stashes the count keyed by `session_id` |
| `pre_llm_call` | First turn of that session | Returns `{"context": "…"}` injecting a one-line "you have N pending shadownet messages" hint. Returns `None` on later turns — observer-only |
| `on_session_end` | End of every `run_conversation` | Drops the stashed count for the session |

Per the guide: `on_session_start`'s return is ignored; only `pre_llm_call`
can inject context into the user message.

## Logout / reconnect

```sh
hermes shadownet logout                  # clears ~/.hermes/.env + config.yaml
docker compose restart hermes            # or `hermes gateway restart` on bare metal

# To reconnect:
echo 'SHADOWNET_CONNECT_URL=shadownet://connect?base=<sidecar>&token=<minted>' >> ~/.hermes/.env
hermes shadownet sync                    # re-writes mcp_servers.shadownet + re-materializes skills
hermes gateway restart
```

## Configuration reference

The plugin reads its config from environment variables (or
`~/.hermes/.env`). One of `SHADOWNET_CONNECT_URL` or `SHADOWNET_TOKEN`
must be set; the rest have sensible defaults.

| Variable | Required | Default | Notes |
| --- | --- | --- | --- |
| `SHADOWNET_CONNECT_URL` | one of these | — | Full `shadownet://connect?base=…&token=…` URL — single value carries both base and token. What the sidecar's connect page hands you. |
| `SHADOWNET_TOKEN` | one of these | — | Account bearer token (use instead of `SHADOWNET_CONNECT_URL` for separate values). |
| `SHADOWNET_SIDECAR_BASE_URL` | no | `https://app.sh4dow.org` | Override for self-hosted sidecars. Ignored when `SHADOWNET_CONNECT_URL` is set. |
| `SHADOWNET_LONG_POLL_TIMEOUT_SECONDS` | no | `30` | Per-call timeout for the inbox long-poll. Server clamps to ≤90s. |
| `SHADOWNET_NOTIFY_CHAT` | no | — | Cross-platform notification target for plan events, format `platform:chat_id` (e.g. `telegram:123456`). When set, inbound coordination/`task.update` events are injected into this chat session so the user sees them. |

## How inbound works (no NAT problem)

Inbound A2A messages are delivered via the `social_inbox_wait` MCP tool
([RFC-0007 amendment D](https://github.com/shadownet-protocol/shadownet-specs)):

1. The plugin opens an MCP session against `<base>/u/<shadowname>/mcp` —
   this is **outbound** from the user's machine, so no public URL or
   NAT traversal needed.
2. A background `asyncio.Task` calls `social_inbox_wait(timeout=30,
   last_event_id=…)` in a loop. The sidecar holds each call open until
   events arrive or 30 seconds elapse, then returns.
3. Each `inbox.message` / `task.update` event is converted to a Hermes
   `MessageEvent` and dispatched to `self.handle_message(...)` — the
   same path Telegram and other platform adapters use.

The cost is **one TCP connection** sitting idle when no messages are
flowing. Comparable to Telegram's default long-polling mode.

## Provider-agnostic

The plugin contains **no `app.sh4dow.org` strings** outside the
single `_adapter.DEFAULT_BASE_URL` constant. Every install can point
at any RFC-0007-compliant sidecar by setting `SHADOWNET_SIDECAR_BASE_URL`
(or by minting a `shadownet://connect?base=…` URL against a different
sidecar).

## Why the shim repo exists

Hermes' `hermes plugins install owner/repo` flow is git-clone-only — it
doesn't run pip on the cloned tree. Our adapter imports `mcp.client.session`
and the `shadownet` SDK transitively, so a naive `plugins install`
pointed at this monorepo would fail with `ModuleNotFoundError`. The
[`shadownet-protocol/hermes-plugin`](https://github.com/shadownet-protocol/hermes-plugin)
satellite repo is the bridge: ~60 LOC, `plugin.yaml` + `__init__.py`
at the root, bootstraps this PyPI package on first `register()`.

## Troubleshooting

| Symptom | Check |
| --- | --- |
| Agent doesn't see `mcp_shadownet_*` tools | `hermes shadownet status` — is `mcp_servers.shadownet` written to `config.yaml`? If not, `hermes shadownet sync` then restart. |
| `/shadownet-*` commands missing from `/help` | `hermes plugins list` — is `shadownet` listed and enabled? Tail `~/.hermes/logs/agent.log` for `register()` failures. |
| `hermes shadownet doctor` reports MCP endpoint unreachable | Verify your sidecar is up and the `SHADOWNET_CONNECT_URL` token is current. |
| No inbox nudge on first turn | The platform you're chatting on may be the shadownet platform itself (auto-suppressed), or there were no pending messages at session start. |

## Updating

```sh
pip install --upgrade shadownet-hermes-plugin
hermes gateway restart
```

## License

MIT.
