Standalone Hermes Agent plugin and CLI for deterministic terminal/tool-output compaction.
Project description
Noisegate
Gate the noise. Keep the signal.
Noisegate is a small, deterministic compaction layer for Hermes Agent. It catches the terminal walls, test-log avalanches, build spam, and other context-gremlins before they flood your model window.
It does not call a model. It does not vibe-summarize your logs. It does not pretend a chopped-off wall of text is "analysis".
It keeps the useful bits, leaves exact content alone, and gets out of the way when it is not sure.
uvx --from noisegate-hermes noisegate install-hermes
That is the normal install/update path for an existing Hermes Agent setup.
The problem
Agents are great at running tools.
Tools are great at producing nonsense amounts of output. Truly Olympic levels of nonsense.
A single test run, package install, Docker build, or search command can dump thousands of lines into context. Most of it is noise. Some of it matters. If you blindly truncate it, you lose the useful part. If you blindly keep it, you burn context and make the next model call worse.
Noisegate sits in the middle:
noisy tool output -> Noisegate -> compact signal-rich result
It is built for agent work where the context window is valuable and exactness matters.
What you get
Noisegate gives you two surfaces:
- a Hermes plugin that can compact noisy tool results before they enter the conversation
- a CLI you can use directly in terminals, scripts, CI jobs, and smoke tests
It knows how to reduce common noisy outputs:
pytestandunittestnpm,pnpm, andyarngit statusandgit log- search output from
rg,grep,ag, andack - Docker build-style logs
- generic long output with deterministic head/tail fallback
And it refuses to touch things that should stay exact:
- file reads
- patches and diffs
- skill documents
- memory, LCM, Hindsight, MCP, search, and web extraction results
- unknown future tools unless explicitly allowed
That last bit matters. A compactor that damages retrieved context is worse than no compactor.
Install or update
Noisegate is distributed as the Python package noisegate-hermes. The Hermes plugin must be installed into the same Python environment that runs hermes.
Most users want this:
uvx --from noisegate-hermes noisegate install-hermes
Use the same command for first install and updates.
It finds hermes on PATH, resolves the Python environment that actually runs Hermes, installs noisegate-hermes there, enables the noisegate plugin, and runs noisegate doctor.
Preview the exact commands first:
uvx --from noisegate-hermes noisegate install-hermes --dry-run
If Hermes is running as a long-lived gateway/service, restart or reload that Hermes process through your normal maintenance flow after installing so the plugin/config change is picked up. Do not kick the chair out from under in-flight agent work.
From a checkout, install that exact checkout into Hermes:
uv run noisegate install-hermes --package .
Noisegate registers two Hermes hooks:
transform_terminal_output
transform_tool_result
npm wrapper
The npm package is a thin installer wrapper. The Python package is still the real thing.
If you prefer npm-shaped commands:
npx -p noisegate-hermes noisegate install-hermes
If your npm client does not resolve the single-bin shortcut, use:
npx -p noisegate-hermes noisegate-hermes-installer install-hermes
Manual fallback for unusual Hermes launchers
Prefer noisegate install-hermes. Use this only when you need to inspect or reproduce the low-level install flow manually.
HERMES_PYTHON="$(head -1 "$(command -v hermes)" | sed 's/^#!//')"
case "$(basename "$HERMES_PYTHON")" in
python*|pypy*) ;;
*) echo "Hermes launcher is not a Python console script; use noisegate install-hermes" >&2; exit 1 ;;
esac
HERMES_PREFIX="$(dirname "$(dirname "$HERMES_PYTHON")")"
if [ ! -f "$HERMES_PREFIX/pyvenv.cfg" ]; then
echo "Hermes Python is not inside a virtual environment; use noisegate install-hermes" >&2
exit 1
fi
uv pip install --python "$HERMES_PYTHON" noisegate-hermes
"$HERMES_PYTHON" - <<'PY'
from hermes_cli.config import load_config, save_config
cfg = load_config()
plugins = cfg.setdefault("plugins", {})
enabled = plugins.get("enabled") if isinstance(plugins.get("enabled"), list) else []
disabled = plugins.get("disabled") if isinstance(plugins.get("disabled"), list) else []
if "noisegate" not in enabled:
enabled.append("noisegate")
plugins["enabled"] = enabled
plugins["disabled"] = [name for name in disabled if name != "noisegate"]
save_config(cfg)
PY
"$HERMES_PYTHON" -m noisegate.cli doctor
Try it in 30 seconds
Run the health check:
noisegate doctor
Compact a noisy stream:
python - <<'PY' | noisegate reduce --command "pytest"
for i in range(300):
print(f"collecting test line {i:03d}")
print("FAILED tests/test_example.py::test_signal")
print("AssertionError: expected signal, got noise")
PY
Wrap a command and keep its exit code:
noisegate wrap -- pytest -q
Bypass compaction when exact output matters:
NOISEGATE_BYPASS=1 noisegate reduce < exact-output.txt
noisegate wrap --raw -- ./script-that-must-stay-exact
noisegate wrap --full -- ./another-exact-command
CLI reference
noisegate reduce --command "pytest" < noisy.log
noisegate reduce-json < hermes-tool-result.json
noisegate wrap -- pytest -q
noisegate wrap --store-artifact -- pytest -q
noisegate wrap --raw -- cat exact-output.txt
noisegate install-hermes --dry-run
noisegate doctor
noisegate cat ng_<artifact-id>
noisegate cat --artifact-dir /tmp/noisegate-artifacts ng_<artifact-id>
noisegate artifacts list --artifact-dir /tmp/noisegate-artifacts
noisegate artifacts stats --artifact-dir /tmp/noisegate-artifacts
noisegate artifacts verify --artifact-dir /tmp/noisegate-artifacts
wrap runs a command without a shell, captures stdout and stderr in arrival order, writes the compacted result to stdout, and exits with the wrapped command's exit code. It captures up to 4 MiB of combined output by default; use --max-capture-bytes <n> to change that.
If capture is truncated, Noisegate adds this marker:
[noisegate: capture truncated]
reduce-json accepts either a Hermes-like envelope with a result string or a direct JSON tool result. Bad JSON fails open and is written back unchanged.
For AI agents
If you are an AI agent working in this repo, read this first.
One-line instruction:
Install and enable Noisegate for Hermes, then stop dumping terminal walls into context; use NOISEGATE_BYPASS when exact bytes matter.
Operational rules:
- Use Noisegate for noisy terminal/tool output, not for exact source material.
- Do not compact file reads, patches, diffs, retrieved context, skill docs, memory results, MCP results, or web extraction output.
- Do not treat Noisegate as a raw-output archive. Raw artifacts are off by default.
- Keep Hermes-LCM optional. Noisegate must work without it.
- Do not write raw terminal output into Hindsight.
- If compaction fails, preserve the original output.
- Before committing, run the quality gate and scan the diff for secrets and personal/private data.
Safe smoke test for a lane or installation:
noisegate doctor
python - <<'PY' | noisegate reduce --command "pytest"
for i in range(260):
print(f"line {i:03d}")
print("FAILED tests/test_demo.py::test_keeps_signal")
print("AssertionError: signal survived")
PY
printf '%s\n' '[noisegate:bypass]' 'line that must stay exact' | noisegate reduce --command "pytest"
Expected result:
doctorreports a healthy package/plugin state- long noisy output gets smaller
- the failure line stays visible
- bypass output stays unchanged
- no config writes, git writes, service restarts, or artifact writes happen unless explicitly requested
- install-hermes dry-runs show exact install/enable commands before changing Hermes
Safety model
Noisegate is intentionally conservative. Paranoid, even. That is a feature.
Compacted output is kept within the configured max_chars and max_lines caps. If a readable omission marker plus preserved content cannot fit, Noisegate leaves the original output unchanged instead of emitting marker fragments or dropping important failure text.
For Hermes hook traffic, it compacts only this explicit allowlist:
terminal
process
read_terminal
browser_console
Protected surfaces include:
read_file
write_file
patch
apply_patch
skill_view
skill_manage
session_search
memory
hindsight_*
lcm_*
mcp_* / mcp__*
web_search
web_extract
execute_code
search_files
git diff / unified diffs
unknown future tools
Bypass controls:
NOISEGATE_BYPASS
NOISEGATE_RAW
[noisegate:bypass]
[noisegate:raw]
Environment flags:
NOISEGATE_DISABLE=1 # turn compaction off
NOISEGATE_ARTIFACTS=1 # opt in to private raw-output artifacts
NOISEGATE_ARTIFACT_DIR=/path/to/artifacts
NOISEGATE_ARTIFACT_SIZE_CAP=1000000 # max stored raw-output bytes per artifact
noisegate doctor reports ignored or fallback environment values, so typos like NOISEGATE_ARTIFACTS=maybe do not fail silently.
Hermes calls transform_terminal_output before its built-in terminal redaction pass. Noisegate still compacts inline terminal output there, but it disables raw artifact storage on that early hook so pre-redaction output is not persisted.
Artifacts
Raw terminal output is not stored by default.
When artifact mode is enabled, Noisegate writes the original output to a private filesystem store:
- directory mode
0700 - file mode
0600 - default size cap of 1,000,000 bytes
- content-addressed IDs shaped like
ng_<sha256-prefix> - path containment and symlink traversal checks
Retrieve an artifact:
noisegate cat ng_<artifact-id>
Inspect the store:
noisegate artifacts list --artifact-dir /tmp/noisegate-artifacts
noisegate artifacts stats --artifact-dir /tmp/noisegate-artifacts
noisegate artifacts verify --artifact-dir /tmp/noisegate-artifacts
verify recomputes hashes and returns a non-zero exit code if an artifact was tampered with, has an invalid private-store path, or if a live temp artifact file is still present. Stale temp files created by interrupted writes are removed during verification/new writes without printing their raw contents.
Hermes-LCM and memory layers
Noisegate is not Hermes-LCM and does not require Hermes-LCM.
If Hermes-LCM is installed, Noisegate acts as an inline compaction step before noisy output inflates active context. Downstream context or transcript layers usually see the compacted result.
Hindsight is semantic long-term memory, not a raw output bucket. Noisegate must not write raw terminal output into Hindsight.
Development
Start clean:
git status --short --branch
git remote -v
Run the local quality gate:
uv run ruff check .
uv run python -m pytest -q
uv run python scripts/check_release.py
uv run python scripts/check_contributors.py
(cd npm/noisegate && npm ci --ignore-scripts && npm test && npm pack --dry-run)
rm -rf dist
uv build
uvx twine check dist/*
git diff --check
Release and publishing
Noisegate uses release-cycle publishing, not random local tokens and crossed fingers:
- PyPI package:
noisegate-hermes - npm package:
noisegate-hermesinstaller wrapper - the main release workflow publishes the GitHub Release, then PyPI, then npm
- npm publish waits until the matching PyPI version is visible
- GitHub Actions publish through OIDC/trusted publishing where supported
- npm publish uses provenance (
npm publish --provenance) - no long-lived publish tokens in git or workflow files
mainis branch-protected; release changes go through PR review
Release helpers:
uv run python scripts/prepare_release.py 0.2.0
uv run python scripts/check_release.py --tag v0.2.0
uv run python scripts/build_release_notes.py v0.2.0 --repo Tosko4/noisegate --output dist/release-notes.md
build_release_notes.py combines the versioned changelog section with GitHub PR metadata for the tag range. Release notes must include update instructions, categorized PRs, each PR's OP/author handle, and first-time contributors whose first merged PR landed since the previous release.
Release metadata must stay aligned across:
pyproject.toml->[project].versionnoisegate/_version.py->__version__noisegate/plugin.yaml-> plugin manifestversionuv.lock-> locked editable project versionnpm/noisegate/package.json-> npm wrapper versionnpm/noisegate/package-lock.json-> npm wrapper lockfile version
GitHub Actions run linting, tests, release metadata checks, contributor checks, package build, twine check, npm wrapper checks, npm dry-run packing, and wheel install smoke tests on Python 3.11, 3.12, and 3.13.
What Noisegate is not
Noisegate is not a model summarizer.
It is not a replacement for logs.
It is not a database.
It is not a magic context engine.
It is a small gate in front of noisy agent output. That is the whole point.
Attribution
Noisegate was informed by the MIT-licensed Tokenjuice project, especially its ideas around deterministic reducers, command classification, safe bypasses, artifact opt-in, and machine-readable metadata.
Noisegate is a fresh Python implementation focused on Hermes Agent.
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file noisegate_hermes-0.1.2.tar.gz.
File metadata
- Download URL: noisegate_hermes-0.1.2.tar.gz
- Upload date:
- Size: 2.6 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ae258d15e54ffee2b97908afd1e23206ddf65ab208e7a9c6744e59d2d6585cf8
|
|
| MD5 |
ceda6d64e64a91f64187f8ea24cbed6d
|
|
| BLAKE2b-256 |
36bb0b5117ba9f160bc5deb7b5a32635243d609954100fb283c04d971a3f999b
|
Provenance
The following attestation bundles were made for noisegate_hermes-0.1.2.tar.gz:
Publisher:
release.yml on Tosko4/noisegate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
noisegate_hermes-0.1.2.tar.gz -
Subject digest:
ae258d15e54ffee2b97908afd1e23206ddf65ab208e7a9c6744e59d2d6585cf8 - Sigstore transparency entry: 2107405392
- Sigstore integration time:
-
Permalink:
Tosko4/noisegate@833a46ee38bd7b4a15762d7059029c38c41e8665 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Tosko4
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@833a46ee38bd7b4a15762d7059029c38c41e8665 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file noisegate_hermes-0.1.2-py3-none-any.whl.
File metadata
- Download URL: noisegate_hermes-0.1.2-py3-none-any.whl
- Upload date:
- Size: 33.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
df8668b98cebd469f08f38158451baead8d9b58d9cbe95ac8ca643d109e7e8c6
|
|
| MD5 |
7bd5157b3e2e67a4fab7a6147bd7072c
|
|
| BLAKE2b-256 |
fab98d4cb964de40f4d9cff81dbae75182735cd0a8a795bf8611763d4843930a
|
Provenance
The following attestation bundles were made for noisegate_hermes-0.1.2-py3-none-any.whl:
Publisher:
release.yml on Tosko4/noisegate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
noisegate_hermes-0.1.2-py3-none-any.whl -
Subject digest:
df8668b98cebd469f08f38158451baead8d9b58d9cbe95ac8ca643d109e7e8c6 - Sigstore transparency entry: 2107405539
- Sigstore integration time:
-
Permalink:
Tosko4/noisegate@833a46ee38bd7b4a15762d7059029c38c41e8665 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Tosko4
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@833a46ee38bd7b4a15762d7059029c38c41e8665 -
Trigger Event:
workflow_dispatch
-
Statement type: