Skip to main content

Pushary Plugin for Hermes Agent

Push notifications and human-in-the-loop for Hermes Agent via Pushary.

Install

These are operator-only shell steps:

npx pushary@latest setup --agents hermes

That writes this plugin to ~/.hermes/plugins/pushary, the folder where Hermes keeps your own plugins. The folder survives hermes update. After pairing with the phone app, setup turns the plugin on with Hermes's own commands: hermes plugins enable pushary, then hermes config set to select Pushary as the approval transport. Existing credentials are reused. Connecting Hermes in the Pushary Mac app does the same.

Restart Hermes so it loads the plugin, then check it:

hermes gateway restart
npx pushary@latest doctor

If you installed it from Hermes's catalog (hermes plugins install pushary), the plugin is already present. If the install did not ask for PUSHARY_API_KEY, save it with hermes config set PUSHARY_API_KEY <your key>. Then select the approval transport below. Never install a second copy over an existing one.

Install by hand

Copy the pushary_plugin folder from this repository into Hermes's plugins folder, turn it on, and restart Hermes:

git clone --depth 1 https://github.com/Pushary/hermes-plugin-pushary /tmp/hermes-plugin-pushary
mkdir -p ~/.hermes/plugins
cp -R /tmp/hermes-plugin-pushary/pushary_plugin ~/.hermes/plugins/pushary
hermes config set security.approval.transport pushary
hermes config set security.approval.transport_fallback builtin
hermes plugins enable pushary
hermes gateway restart

Hermes v0.21.6 and later manage their own Python, and Hermes does not support installing plugins into it with pip. Only a Hermes older than v0.21.6 still runs from its own virtualenv. There, you can install the package with that virtualenv's Python and add pushary to plugins.enabled:

~/.hermes/hermes-agent/venv/bin/python -m pip install hermes-plugin-pushary==0.8.0

Setup

The plugin reads PUSHARY_API_KEY through Hermes, which looks in the active profile's .env (~/.hermes/.env by default) and in the environment Hermes runs with. hermes config set PUSHARY_API_KEY <your key> saves it there. A copy installed from Hermes's catalog, or copied in by hand, uses only that.

A copy that npx pushary setup or the Pushary Mac app installed, and a pip install, also fall back to the key in ~/.pushary/config.json, which is where setup and the Mac app keep it.

What it reads and sends

  • Network. Only pushary.com, or PUSHARY_BASE_URL when set. Session events on every turn, with a machine id. The questions you send. The approvals Hermes routes to Pushary, which carry the command (up to 4,000 characters) and its description after Hermes has redacted secrets from both. Gated tool arguments and at most three tool errors per session, redacted by Hermes first.
  • Machine id. ~/.pushary/machine-id when it exists, so Hermes and your other agents count as one machine. Otherwise a hash of the hostname.
  • Files. Messages queued in the Pushary app wait in ~/.pushary/run/hermes-commands until a turn picks them up. The plugin notes which Hermes process loaded it in plugin-data/pushary/loaded/ in the Hermes home.
  • Credentials. PUSHARY_API_KEY, as described under Setup.
  • Usage report. Once each time Hermes starts, the plugin reports that it loaded, with the plugin and Hermes versions. A copy from Hermes's catalog, or one copied into Hermes's plugins folder by hand, never sends it.

Tools

Tool Description
pushary_notify Send a push notification with optional rich context
pushary_remind Schedule, list or cancel a personal reminder saved on Pushary after Hermes exits
pushary_ask Ask a question via push (yes/no, multiple choice, or free text)
pushary_wait Wait for an existing question; repeat while pending with the same correlation ID
pushary_cancel Cancel a pending question
pushary_propose_scope Agree the boundary of a run once: file globs, or a recorded promise when it changes no files
pushary_enroll Connect one of your own end-users' phones (Partner plan)
pushary_ask_end_user Ask one of your own end-users, fail-closed (Partner plan)

Approving from your phone

Hermes already detects dangerous commands and asks a human before running them. This plugin registers pushary as an approval transport, so that question goes to your phone instead of a terminal nobody is watching.

# ~/.hermes/config.yaml
security:
  approval:
    transport: pushary
    transport_fallback: builtin

Open the app to see the same four choices Hermes offers in the terminal. Hermes remembers the last two exactly as it would have:

Choice Effect
Allow once Runs this command
Allow for this session Runs it, and stops asking for the rest of the session
Always allow Runs it, and writes a standing allow rule
Deny Blocks it, and the agent is told the user did not consent

transport_fallback: builtin is what makes this safe to leave on: if your API key is missing, or Terminal mode/no reachable device requests a local handoff, Hermes can fall back after Pushary safely withdraws its question. Cancellation or unverifiable withdrawal stops the action; a network failure while waiting does not silently reopen approval. Drop the fallback line and an unreachable Pushary becomes a denial instead.

How long your phone has depends on your delivery mode. Under Every time (push_only), the default for a new workspace, it is Hermes' own approvals.timeout (300 seconds by default), so an answer is still yours to give minutes after the notification lands. Under When I'm out (push_first), your phone has the push window, 20 seconds unless you change it, and then Hermes' own prompt takes over.

Per-tool gating (without the transport)

To gate specific tools rather than Hermes' dangerous-command set, set PUSHARY_GATE_TOOLS. This runs inside the pre_tool_call hook.

export PUSHARY_GATE_TOOLS="terminal,write_file"
export PUSHARY_GATE_TIMEOUT_MS=25000
Outcome Result
You approve Tool runs
You deny Tool is blocked, the agent is told why
No answer in the window Tool is blocked (fail-closed)
Pushary unreachable / no API key Tool is blocked (fail-closed)

When no phone, browser, or Slack channel is connected, Pushary says so on the create call rather than waiting, and the gate blocks with that as the reason instead of something that reads like a refusal.

The window here is bounded by Hermes, not by us. Hermes abandons any pre_tool_call callback that outlives plugins.hook_callback_timeout (30 seconds by default) and fails it closed, so the plugin clamps its own wait to fit inside that budget and returns a real answer rather than being cut off mid-wait. Raising plugins.hook_callback_timeout raises the window; the plugin reads it and follows. A Hermes old enough to have no callback timeout at all runs the hook to completion, and there the plugin uses the full window rather than shortening it for a deadline that does not exist.

When both are configured the transport wins: the hook escalates to Hermes' own approval gate, which gets the full 300-second window and the once/session/always choices, instead of holding a 30-second hook open on a network call.

For an escalated per-tool gate, Always allow creates a standing rule for the whole tool name, covering its future calls with different arguments. Choose Allow once when only the displayed call is authorized.

Auto-notifications

Errors returned by any tool are pushed automatically, capped at three per session. Set PUSHARY_AUTO_NOTIFY_SESSION_END=1 to also get one notification when a session finishes, with its tool and error counts.

Gated argument summaries, targets and error text use Hermes's forced secret redaction before truncation or transmission to pushary.com. If the host redactor is unavailable or fails, the detail is omitted.

Set PUSHARY_AGENT_NAME to identify this Hermes instance in notifications (e.g. "Hermes - daily-briefing"). It defaults to Hermes.

Messages from Pushary

An active Hermes session appears in the macOS notch and dashboard. A message sent there is injected into that session on its next turn. Hermes plugins cannot wake a CLI that is already idle, so the message remains queued until that session receives its next prompt.

Tests

PYTHONPATH=. python3 -m unittest discover -s tests

tests/test_hermes_contract.py checks this plugin's assumptions against the Hermes it is running inside: that every hook it registers is one Hermes fires, that the callback budget it clamps to is the one Hermes enforces, and that the approval request still carries what the transport reads. It skips where Hermes is not importable, so run it with the interpreter Hermes uses, and after a Hermes upgrade. On a Hermes older than v0.21.6, that interpreter is in its virtualenv:

PYTHONPATH=. ~/.hermes/hermes-agent/venv/bin/python3 -m unittest discover -s tests

License

MIT

Contributing

This public repository is a source snapshot maintained from Pushary's source monorepo. Open issues and pull requests here; accepted changes are applied upstream and then mirrored back. Do not publish registry releases from this mirror.

Use Python 3.10 or newer:

python -m pip install . build twine
python -m unittest discover -s . -p 'test_*.py'
python -m unittest discover -s tests -p 'test_*.py'
python -m build
python -m twine check dist/*

Host contract tests skip when Hermes is not installed. Run them in the Hermes environment after upgrading the host.

Customer answers and routing

Personal tools reach the operator on their configured phone, Mac or browser. Partner tools target a customer by external_id; they do not create a customer inbox in the Mac app. For pushary_ask_end_user, read answered and value for choice/text results. approved is true only for an answered affirmative confirm; a choice or text containing “yes” does not authorize an action. Polling waits and notification routes follow server policy; never interpret silence as consent.

Metadata

Release files for hermes-plugin-pushary 0.8.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for hermes-plugin-pushary 0.8.0
File Size Uploaded Provenance
hermes_plugin_pushary-0.8.0.tar.gz 45.2 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for hermes-plugin-pushary 0.8.0
File Interpreter ABI Platform
hermes_plugin_pushary-0.8.0-py3-none-any.whl Python 3 none any Details

Total release size: 80.9 kB

Release files / hermes_plugin_pushary-0.8.0.tar.gz

Download URL hermes_plugin_pushary-0.8.0.tar.gz
Size 45.2 kB
Tags Source
SHA-256 checksum
How to use checksums
adb6e4e7bfcc32ee4264db6783bc4d15ed4fa43ad5e822db00493aec7e2c9ba5
BLAKE2b-256 checksum
How to use checksums
9f840f4034a94a70763e8d093bf7a7f81f699a5efb343c388be38791bb3c9c5d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Release files / hermes_plugin_pushary-0.8.0-py3-none-any.whl

Download URL hermes_plugin_pushary-0.8.0-py3-none-any.whl
Size 35.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6e1c8c6076e09f5ab6b06045cc3ee20122aeb56431e505ea0476ebff33c3b239
BLAKE2b-256 checksum
How to use checksums
c2137a38b4d49aad44effb1437f0e9f5ad0b7aeb98e8729570dc6c7066ba3d57
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Security for hermes-plugin-pushary 0.8.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for hermes_plugin_pushary-0.8.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page