atbash-hermes-plugin 0.12.0.dev0__tar.gz → 0.15.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.
- {atbash_hermes_plugin-0.12.0.dev0/atbash_hermes_plugin.egg-info → atbash_hermes_plugin-0.15.0}/PKG-INFO +63 -5
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/README.md +61 -3
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/atbash_hermes_plugin/__init__.py +7 -21
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0/atbash_hermes_plugin.egg-info}/PKG-INFO +63 -5
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/atbash_hermes_plugin.egg-info/requires.txt +1 -1
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/plugin.yaml +1 -1
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/pyproject.toml +2 -2
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_memory_poisoning.py +0 -7
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_pre_tool_call_verdicts.py +1 -1
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_zero_bugs_issues.py +0 -7
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_zero_bugs_more.py +0 -7
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_zero_bugs_round.py +2 -12
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/LICENSE +0 -0
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/MANIFEST.in +0 -0
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/NOTICE +0 -0
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/atbash_hermes_plugin/__main__.py +0 -0
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/atbash_hermes_plugin.egg-info/SOURCES.txt +0 -0
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/atbash_hermes_plugin.egg-info/dependency_links.txt +0 -0
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/atbash_hermes_plugin.egg-info/entry_points.txt +0 -0
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/atbash_hermes_plugin.egg-info/top_level.txt +0 -0
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/setup.cfg +0 -0
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_guard_selfprotection.py +0 -0
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_memory_write_fail_closed.py +0 -0
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_release_contract.py +0 -0
- {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_zero_bugs_fuzz.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: atbash-hermes-plugin
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.15.0
|
|
4
4
|
Summary: Atbash safety plugin for Hermes Agent
|
|
5
5
|
Author: atbash
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -11,7 +11,7 @@ Requires-Python: <3.13,>=3.9
|
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
12
|
License-File: LICENSE
|
|
13
13
|
License-File: NOTICE
|
|
14
|
-
Requires-Dist: atbash-sdk<1.0.0,>=0.
|
|
14
|
+
Requires-Dist: atbash-sdk<1.0.0,>=0.15.0
|
|
15
15
|
Requires-Dist: httpx<1,>=0.27
|
|
16
16
|
Requires-Dist: opentelemetry-exporter-otlp-proto-http<2,>=1.29
|
|
17
17
|
Requires-Dist: opentelemetry-sdk<2,>=1.29
|
|
@@ -294,14 +294,72 @@ repo.
|
|
|
294
294
|
For `HOLD`, the user-facing block message is:
|
|
295
295
|
|
|
296
296
|
```text
|
|
297
|
-
Action
|
|
297
|
+
Action flagged as a policy gap. The agent will not be jailed — please adjust your policy in the Atbash dashboard, then ask the agent to try again.
|
|
298
298
|
Reason: <original reason from judge>
|
|
299
299
|
Tool Call ID: <held judgment id, when provided by the SDK>
|
|
300
300
|
```
|
|
301
301
|
|
|
302
|
-
After
|
|
302
|
+
After the policy is adjusted in the dashboard, the SDK can query held-action status with
|
|
303
303
|
the judgment ID. The Hermes plugin blocks the original tool call and asks the
|
|
304
|
-
user to retry after
|
|
304
|
+
user to retry after the policy is updated, rather than automatically re-running the action.
|
|
305
|
+
|
|
306
|
+
## Guard Self-Protection And Memory Baselines
|
|
307
|
+
|
|
308
|
+
- **The guard's own state is not writable by the agent.** Write tools (`write`, `edit`,
|
|
309
|
+
`multiedit`, `write_file`, `create_file`, `patch`, `apply_patch`, `str_replace_editor`,
|
|
310
|
+
`fs_write`, `notebook_edit`, and anything the tool map classes as `filesystem_write`)
|
|
311
|
+
are refused, before judging and in monitor mode too, when they target
|
|
312
|
+
`~/.config/atbash`, the configured tool map, agent key and hash baseline, a dedicated
|
|
313
|
+
key directory (such as `/opt/data/atbash`), the Hermes home's `.env`, `.op.env`,
|
|
314
|
+
`config.yaml` and `SOUL.md` (`~/.hermes`, `$HERMES_HOME`, and `%LOCALAPPDATA%\hermes` on
|
|
315
|
+
Windows), the Hermes install folder's `.env`, or the plugin's install directory. To
|
|
316
|
+
change one of these (for example your persona in `SOUL.md`), edit the file yourself;
|
|
317
|
+
the agent's write is refused even if you approve it inside Hermes. Paths are
|
|
318
|
+
compared by file identity, so hard links and case variants count. Patch tools are also
|
|
319
|
+
refused when their body names one of those paths.
|
|
320
|
+
- **A memory file with no trusted baseline is blocked, and nothing is sent anywhere.**
|
|
321
|
+
A memory file is one of `AGENTS.md`, `AGENTS.override.md`, `CLAUDE.md`, `MEMORY.md`,
|
|
322
|
+
`USER.md`, `DREAMS.md`, `.hermes.md`, `HERMES.md`, `.cursorrules` (by exact name, and on
|
|
323
|
+
Windows also the spellings Windows opens as that file, such as `AGENTS.md.`), a
|
|
324
|
+
`.cursor/rules/*.mdc` file, a `.md`/`.txt` document inside a `memory/` or `memories/`
|
|
325
|
+
folder, or a file under `~/.claude/projects/` or `~/.openclaw/{workspace,memory}/`.
|
|
326
|
+
Ordinary code such as `src/memory/alloc.rs` is not memory and is judged like any other
|
|
327
|
+
file.
|
|
328
|
+
Files the guard records itself (judged writes) are trusted. For files that existed
|
|
329
|
+
before the guard (a repository's `AGENTS.md`), review them and trust them yourself,
|
|
330
|
+
outside the agent:
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
python -m atbash_hermes_plugin seed AGENTS.md memory/notes.md
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Run it from outside the repository you are trusting, with absolute paths (for example
|
|
337
|
+
`cd ~ && python -m atbash_hermes_plugin seed /path/to/repo/AGENTS.md`): `python -m`
|
|
338
|
+
puts the current folder first on Python's import path, so a hostile repository could
|
|
339
|
+
otherwise shadow a standard module. The command itself never imports Hermes code.
|
|
340
|
+
|
|
341
|
+
It prints the baseline file it wrote. That is the one the guard reads:
|
|
342
|
+
`ATBASH_MEMORY_HASH_PATH` from your environment or from the Hermes home `.env`, else
|
|
343
|
+
`~/.config/atbash/hermes-memory-hashes.json`. Pass `--store <file>` to choose it
|
|
344
|
+
explicitly. If any file in the list cannot be read, nothing is trusted.
|
|
345
|
+
|
|
346
|
+
The guard refuses agent tool calls that look like they run the seeder (the command,
|
|
347
|
+
`seed_memory_baselines`, the baseline file's name, including quoted, variable-split or
|
|
348
|
+
base64-encoded forms). This is best effort - see the known limit below - and it also
|
|
349
|
+
refuses harmless agent calls that merely mention those names (editing these docs, a
|
|
350
|
+
grep for the command).
|
|
351
|
+
|
|
352
|
+
In Docker, point the baseline file at the mounted volume so trust survives restarts,
|
|
353
|
+
e.g. `-e ATBASH_MEMORY_HASH_PATH=/opt/data/atbash/hermes-memory-hashes.json`, and run
|
|
354
|
+
the seed command inside the container.
|
|
355
|
+
- **What the guard sees:** tool calls only. Hermes loads its context files (`AGENTS.md`,
|
|
356
|
+
`.hermes.md`, `SOUL.md`, ...) into a new session's instructions without a tool call, so
|
|
357
|
+
baselines protect the agent's tool reads, and the real control on those files is that
|
|
358
|
+
agent writes to them are scanned and judged.
|
|
359
|
+
- **Known limit:** the baseline file (`~/.config/atbash/hermes-memory-hashes.json`) is
|
|
360
|
+
plain JSON. The guard refuses the agent's file tools on it, but a shell command that
|
|
361
|
+
rewrites a memory file and recomputes its SHA-256 is left to the judge. Closing that
|
|
362
|
+
needs an authenticated baseline (a MAC) or state outside the agent's reach.
|
|
305
363
|
|
|
306
364
|
## Guard Self-Protection And Memory Baselines
|
|
307
365
|
|
|
@@ -275,14 +275,72 @@ repo.
|
|
|
275
275
|
For `HOLD`, the user-facing block message is:
|
|
276
276
|
|
|
277
277
|
```text
|
|
278
|
-
Action
|
|
278
|
+
Action flagged as a policy gap. The agent will not be jailed — please adjust your policy in the Atbash dashboard, then ask the agent to try again.
|
|
279
279
|
Reason: <original reason from judge>
|
|
280
280
|
Tool Call ID: <held judgment id, when provided by the SDK>
|
|
281
281
|
```
|
|
282
282
|
|
|
283
|
-
After
|
|
283
|
+
After the policy is adjusted in the dashboard, the SDK can query held-action status with
|
|
284
284
|
the judgment ID. The Hermes plugin blocks the original tool call and asks the
|
|
285
|
-
user to retry after
|
|
285
|
+
user to retry after the policy is updated, rather than automatically re-running the action.
|
|
286
|
+
|
|
287
|
+
## Guard Self-Protection And Memory Baselines
|
|
288
|
+
|
|
289
|
+
- **The guard's own state is not writable by the agent.** Write tools (`write`, `edit`,
|
|
290
|
+
`multiedit`, `write_file`, `create_file`, `patch`, `apply_patch`, `str_replace_editor`,
|
|
291
|
+
`fs_write`, `notebook_edit`, and anything the tool map classes as `filesystem_write`)
|
|
292
|
+
are refused, before judging and in monitor mode too, when they target
|
|
293
|
+
`~/.config/atbash`, the configured tool map, agent key and hash baseline, a dedicated
|
|
294
|
+
key directory (such as `/opt/data/atbash`), the Hermes home's `.env`, `.op.env`,
|
|
295
|
+
`config.yaml` and `SOUL.md` (`~/.hermes`, `$HERMES_HOME`, and `%LOCALAPPDATA%\hermes` on
|
|
296
|
+
Windows), the Hermes install folder's `.env`, or the plugin's install directory. To
|
|
297
|
+
change one of these (for example your persona in `SOUL.md`), edit the file yourself;
|
|
298
|
+
the agent's write is refused even if you approve it inside Hermes. Paths are
|
|
299
|
+
compared by file identity, so hard links and case variants count. Patch tools are also
|
|
300
|
+
refused when their body names one of those paths.
|
|
301
|
+
- **A memory file with no trusted baseline is blocked, and nothing is sent anywhere.**
|
|
302
|
+
A memory file is one of `AGENTS.md`, `AGENTS.override.md`, `CLAUDE.md`, `MEMORY.md`,
|
|
303
|
+
`USER.md`, `DREAMS.md`, `.hermes.md`, `HERMES.md`, `.cursorrules` (by exact name, and on
|
|
304
|
+
Windows also the spellings Windows opens as that file, such as `AGENTS.md.`), a
|
|
305
|
+
`.cursor/rules/*.mdc` file, a `.md`/`.txt` document inside a `memory/` or `memories/`
|
|
306
|
+
folder, or a file under `~/.claude/projects/` or `~/.openclaw/{workspace,memory}/`.
|
|
307
|
+
Ordinary code such as `src/memory/alloc.rs` is not memory and is judged like any other
|
|
308
|
+
file.
|
|
309
|
+
Files the guard records itself (judged writes) are trusted. For files that existed
|
|
310
|
+
before the guard (a repository's `AGENTS.md`), review them and trust them yourself,
|
|
311
|
+
outside the agent:
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
python -m atbash_hermes_plugin seed AGENTS.md memory/notes.md
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Run it from outside the repository you are trusting, with absolute paths (for example
|
|
318
|
+
`cd ~ && python -m atbash_hermes_plugin seed /path/to/repo/AGENTS.md`): `python -m`
|
|
319
|
+
puts the current folder first on Python's import path, so a hostile repository could
|
|
320
|
+
otherwise shadow a standard module. The command itself never imports Hermes code.
|
|
321
|
+
|
|
322
|
+
It prints the baseline file it wrote. That is the one the guard reads:
|
|
323
|
+
`ATBASH_MEMORY_HASH_PATH` from your environment or from the Hermes home `.env`, else
|
|
324
|
+
`~/.config/atbash/hermes-memory-hashes.json`. Pass `--store <file>` to choose it
|
|
325
|
+
explicitly. If any file in the list cannot be read, nothing is trusted.
|
|
326
|
+
|
|
327
|
+
The guard refuses agent tool calls that look like they run the seeder (the command,
|
|
328
|
+
`seed_memory_baselines`, the baseline file's name, including quoted, variable-split or
|
|
329
|
+
base64-encoded forms). This is best effort - see the known limit below - and it also
|
|
330
|
+
refuses harmless agent calls that merely mention those names (editing these docs, a
|
|
331
|
+
grep for the command).
|
|
332
|
+
|
|
333
|
+
In Docker, point the baseline file at the mounted volume so trust survives restarts,
|
|
334
|
+
e.g. `-e ATBASH_MEMORY_HASH_PATH=/opt/data/atbash/hermes-memory-hashes.json`, and run
|
|
335
|
+
the seed command inside the container.
|
|
336
|
+
- **What the guard sees:** tool calls only. Hermes loads its context files (`AGENTS.md`,
|
|
337
|
+
`.hermes.md`, `SOUL.md`, ...) into a new session's instructions without a tool call, so
|
|
338
|
+
baselines protect the agent's tool reads, and the real control on those files is that
|
|
339
|
+
agent writes to them are scanned and judged.
|
|
340
|
+
- **Known limit:** the baseline file (`~/.config/atbash/hermes-memory-hashes.json`) is
|
|
341
|
+
plain JSON. The guard refuses the agent's file tools on it, but a shell command that
|
|
342
|
+
rewrites a memory file and recomputes its SHA-256 is left to the judge. Closing that
|
|
343
|
+
needs an authenticated baseline (a MAC) or state outside the agent's reach.
|
|
286
344
|
|
|
287
345
|
## Guard Self-Protection And Memory Baselines
|
|
288
346
|
|
{atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/atbash_hermes_plugin/__init__.py
RENAMED
|
@@ -18,16 +18,9 @@ from typing import Any, Dict, Iterable, Optional
|
|
|
18
18
|
logger = logging.getLogger("atbash-hermes-plugin")
|
|
19
19
|
_TELEMETRY_SHUTDOWN_REGISTERED = False
|
|
20
20
|
|
|
21
|
-
try:
|
|
22
|
-
from atbash_core import redact_secrets as _redact_secrets # type: ignore
|
|
23
|
-
_REDACT_AVAILABLE = True
|
|
24
|
-
except Exception:
|
|
25
|
-
_redact_secrets = None # type: ignore
|
|
26
|
-
_REDACT_AVAILABLE = False
|
|
27
|
-
|
|
28
21
|
HOLD_MESSAGE = (
|
|
29
|
-
"Action
|
|
30
|
-
"
|
|
22
|
+
"Action flagged as a policy gap. The agent will not be jailed — please adjust "
|
|
23
|
+
"your policy in the Atbash dashboard, then ask the agent to try "
|
|
31
24
|
"again.\nReason: {reason}"
|
|
32
25
|
)
|
|
33
26
|
|
|
@@ -2343,20 +2336,13 @@ class AtbashHermesGuard:
|
|
|
2343
2336
|
# Redact embedded secrets before sending to the judge — mirrors SDK's
|
|
2344
2337
|
# scanMemory → redactSecrets → judgeAction path to prevent secrets inside
|
|
2345
2338
|
# memory content from being signed and posted to chain.
|
|
2346
|
-
if not _REDACT_AVAILABLE:
|
|
2347
|
-
# N-8: unredacted memory content must never be signed and
|
|
2348
|
-
# posted — a broken/partial SDK is a block, not a silent send.
|
|
2349
|
-
logger.warning("Atbash memory write redaction unavailable")
|
|
2350
|
-
if self.fail_closed:
|
|
2351
|
-
return {
|
|
2352
|
-
"action": "block",
|
|
2353
|
-
"message": "Memory write blocked (secret redaction unavailable).",
|
|
2354
|
-
}
|
|
2355
|
-
return None
|
|
2356
2339
|
try:
|
|
2357
|
-
|
|
2340
|
+
from atbash_core import redact_secrets # type: ignore
|
|
2341
|
+
formatted = redact_secrets(formatted).redacted
|
|
2358
2342
|
except Exception as _re:
|
|
2359
|
-
|
|
2343
|
+
# N-8: unredacted memory content must never be signed and
|
|
2344
|
+
# posted — a broken/partial SDK is a block, not a silent send.
|
|
2345
|
+
logger.warning("Atbash memory write redaction unavailable err=%s", _re)
|
|
2360
2346
|
if self.fail_closed:
|
|
2361
2347
|
return {
|
|
2362
2348
|
"action": "block",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: atbash-hermes-plugin
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.15.0
|
|
4
4
|
Summary: Atbash safety plugin for Hermes Agent
|
|
5
5
|
Author: atbash
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -11,7 +11,7 @@ Requires-Python: <3.13,>=3.9
|
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
12
|
License-File: LICENSE
|
|
13
13
|
License-File: NOTICE
|
|
14
|
-
Requires-Dist: atbash-sdk<1.0.0,>=0.
|
|
14
|
+
Requires-Dist: atbash-sdk<1.0.0,>=0.15.0
|
|
15
15
|
Requires-Dist: httpx<1,>=0.27
|
|
16
16
|
Requires-Dist: opentelemetry-exporter-otlp-proto-http<2,>=1.29
|
|
17
17
|
Requires-Dist: opentelemetry-sdk<2,>=1.29
|
|
@@ -294,14 +294,72 @@ repo.
|
|
|
294
294
|
For `HOLD`, the user-facing block message is:
|
|
295
295
|
|
|
296
296
|
```text
|
|
297
|
-
Action
|
|
297
|
+
Action flagged as a policy gap. The agent will not be jailed — please adjust your policy in the Atbash dashboard, then ask the agent to try again.
|
|
298
298
|
Reason: <original reason from judge>
|
|
299
299
|
Tool Call ID: <held judgment id, when provided by the SDK>
|
|
300
300
|
```
|
|
301
301
|
|
|
302
|
-
After
|
|
302
|
+
After the policy is adjusted in the dashboard, the SDK can query held-action status with
|
|
303
303
|
the judgment ID. The Hermes plugin blocks the original tool call and asks the
|
|
304
|
-
user to retry after
|
|
304
|
+
user to retry after the policy is updated, rather than automatically re-running the action.
|
|
305
|
+
|
|
306
|
+
## Guard Self-Protection And Memory Baselines
|
|
307
|
+
|
|
308
|
+
- **The guard's own state is not writable by the agent.** Write tools (`write`, `edit`,
|
|
309
|
+
`multiedit`, `write_file`, `create_file`, `patch`, `apply_patch`, `str_replace_editor`,
|
|
310
|
+
`fs_write`, `notebook_edit`, and anything the tool map classes as `filesystem_write`)
|
|
311
|
+
are refused, before judging and in monitor mode too, when they target
|
|
312
|
+
`~/.config/atbash`, the configured tool map, agent key and hash baseline, a dedicated
|
|
313
|
+
key directory (such as `/opt/data/atbash`), the Hermes home's `.env`, `.op.env`,
|
|
314
|
+
`config.yaml` and `SOUL.md` (`~/.hermes`, `$HERMES_HOME`, and `%LOCALAPPDATA%\hermes` on
|
|
315
|
+
Windows), the Hermes install folder's `.env`, or the plugin's install directory. To
|
|
316
|
+
change one of these (for example your persona in `SOUL.md`), edit the file yourself;
|
|
317
|
+
the agent's write is refused even if you approve it inside Hermes. Paths are
|
|
318
|
+
compared by file identity, so hard links and case variants count. Patch tools are also
|
|
319
|
+
refused when their body names one of those paths.
|
|
320
|
+
- **A memory file with no trusted baseline is blocked, and nothing is sent anywhere.**
|
|
321
|
+
A memory file is one of `AGENTS.md`, `AGENTS.override.md`, `CLAUDE.md`, `MEMORY.md`,
|
|
322
|
+
`USER.md`, `DREAMS.md`, `.hermes.md`, `HERMES.md`, `.cursorrules` (by exact name, and on
|
|
323
|
+
Windows also the spellings Windows opens as that file, such as `AGENTS.md.`), a
|
|
324
|
+
`.cursor/rules/*.mdc` file, a `.md`/`.txt` document inside a `memory/` or `memories/`
|
|
325
|
+
folder, or a file under `~/.claude/projects/` or `~/.openclaw/{workspace,memory}/`.
|
|
326
|
+
Ordinary code such as `src/memory/alloc.rs` is not memory and is judged like any other
|
|
327
|
+
file.
|
|
328
|
+
Files the guard records itself (judged writes) are trusted. For files that existed
|
|
329
|
+
before the guard (a repository's `AGENTS.md`), review them and trust them yourself,
|
|
330
|
+
outside the agent:
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
python -m atbash_hermes_plugin seed AGENTS.md memory/notes.md
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Run it from outside the repository you are trusting, with absolute paths (for example
|
|
337
|
+
`cd ~ && python -m atbash_hermes_plugin seed /path/to/repo/AGENTS.md`): `python -m`
|
|
338
|
+
puts the current folder first on Python's import path, so a hostile repository could
|
|
339
|
+
otherwise shadow a standard module. The command itself never imports Hermes code.
|
|
340
|
+
|
|
341
|
+
It prints the baseline file it wrote. That is the one the guard reads:
|
|
342
|
+
`ATBASH_MEMORY_HASH_PATH` from your environment or from the Hermes home `.env`, else
|
|
343
|
+
`~/.config/atbash/hermes-memory-hashes.json`. Pass `--store <file>` to choose it
|
|
344
|
+
explicitly. If any file in the list cannot be read, nothing is trusted.
|
|
345
|
+
|
|
346
|
+
The guard refuses agent tool calls that look like they run the seeder (the command,
|
|
347
|
+
`seed_memory_baselines`, the baseline file's name, including quoted, variable-split or
|
|
348
|
+
base64-encoded forms). This is best effort - see the known limit below - and it also
|
|
349
|
+
refuses harmless agent calls that merely mention those names (editing these docs, a
|
|
350
|
+
grep for the command).
|
|
351
|
+
|
|
352
|
+
In Docker, point the baseline file at the mounted volume so trust survives restarts,
|
|
353
|
+
e.g. `-e ATBASH_MEMORY_HASH_PATH=/opt/data/atbash/hermes-memory-hashes.json`, and run
|
|
354
|
+
the seed command inside the container.
|
|
355
|
+
- **What the guard sees:** tool calls only. Hermes loads its context files (`AGENTS.md`,
|
|
356
|
+
`.hermes.md`, `SOUL.md`, ...) into a new session's instructions without a tool call, so
|
|
357
|
+
baselines protect the agent's tool reads, and the real control on those files is that
|
|
358
|
+
agent writes to them are scanned and judged.
|
|
359
|
+
- **Known limit:** the baseline file (`~/.config/atbash/hermes-memory-hashes.json`) is
|
|
360
|
+
plain JSON. The guard refuses the agent's file tools on it, but a shell command that
|
|
361
|
+
rewrites a memory file and recomputes its SHA-256 is left to the judge. Closing that
|
|
362
|
+
needs an authenticated baseline (a MAC) or state outside the agent's reach.
|
|
305
363
|
|
|
306
364
|
## Guard Self-Protection And Memory Baselines
|
|
307
365
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "atbash-hermes-plugin"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.15.0"
|
|
8
8
|
description = "Atbash safety plugin for Hermes Agent"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9,<3.13"
|
|
@@ -24,7 +24,7 @@ keywords = [
|
|
|
24
24
|
"policy"
|
|
25
25
|
]
|
|
26
26
|
dependencies = [
|
|
27
|
-
"atbash-sdk>=0.
|
|
27
|
+
"atbash-sdk>=0.15.0,<1.0.0",
|
|
28
28
|
"httpx>=0.27,<1",
|
|
29
29
|
"opentelemetry-exporter-otlp-proto-http>=1.29,<2",
|
|
30
30
|
"opentelemetry-sdk>=1.29,<2",
|
{atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_memory_poisoning.py
RENAMED
|
@@ -14,13 +14,6 @@ import unittest
|
|
|
14
14
|
import unittest.mock as mock
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
|
|
17
|
-
if "atbash_core" not in sys.modules:
|
|
18
|
-
_stub = types.ModuleType("atbash_core")
|
|
19
|
-
_stub.redact_secrets = lambda text: types.SimpleNamespace(redacted=text)
|
|
20
|
-
_stub.create_memory_snapshot = lambda entries: None
|
|
21
|
-
_stub.diff_memory_snapshots = lambda *a, **k: None
|
|
22
|
-
sys.modules["atbash_core"] = _stub
|
|
23
|
-
|
|
24
17
|
_MODULE_PATH = Path(__file__).resolve().parents[1] / "atbash_hermes_plugin" / "__init__.py"
|
|
25
18
|
_plugin = types.ModuleType("atbash_hermes_plugin_under_test")
|
|
26
19
|
_plugin.__file__ = str(_MODULE_PATH)
|
|
@@ -462,7 +462,7 @@ class LegitimateOutcomesUnchanged(unittest.TestCase):
|
|
|
462
462
|
def test_hold_blocks_with_review_message(self):
|
|
463
463
|
result = call(make_guard(decision=FakeDecision("HOLD")))
|
|
464
464
|
self.assertTrue(is_blocked(result))
|
|
465
|
-
self.assertIn("
|
|
465
|
+
self.assertIn("flagged as a policy gap", result["message"].lower())
|
|
466
466
|
|
|
467
467
|
def test_block_words_block(self):
|
|
468
468
|
for verdict in ["BLOCK", "DENY", "REJECT", "DISALLOW"]:
|
{atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_zero_bugs_issues.py
RENAMED
|
@@ -17,13 +17,6 @@ import unittest
|
|
|
17
17
|
import unittest.mock
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
|
|
20
|
-
if "atbash_core" not in sys.modules:
|
|
21
|
-
_stub = types.ModuleType("atbash_core")
|
|
22
|
-
_stub.redact_secrets = lambda text: types.SimpleNamespace(redacted=text)
|
|
23
|
-
_stub.create_memory_snapshot = lambda entries: None
|
|
24
|
-
_stub.diff_memory_snapshots = lambda *a, **k: None
|
|
25
|
-
sys.modules["atbash_core"] = _stub
|
|
26
|
-
|
|
27
20
|
_MODULE_PATH = Path(__file__).resolve().parents[1] / "atbash_hermes_plugin" / "__init__.py"
|
|
28
21
|
_plugin = types.ModuleType("atbash_hermes_plugin_zero_bugs_issues")
|
|
29
22
|
_plugin.__file__ = str(_MODULE_PATH)
|
{atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_zero_bugs_more.py
RENAMED
|
@@ -15,13 +15,6 @@ import types
|
|
|
15
15
|
import unittest
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
|
|
18
|
-
if "atbash_core" not in sys.modules:
|
|
19
|
-
_stub = types.ModuleType("atbash_core")
|
|
20
|
-
_stub.redact_secrets = lambda text: types.SimpleNamespace(redacted=text)
|
|
21
|
-
_stub.create_memory_snapshot = lambda entries: None
|
|
22
|
-
_stub.diff_memory_snapshots = lambda *a, **k: None
|
|
23
|
-
sys.modules["atbash_core"] = _stub
|
|
24
|
-
|
|
25
18
|
_MODULE_PATH = Path(__file__).resolve().parents[1] / "atbash_hermes_plugin" / "__init__.py"
|
|
26
19
|
_plugin = types.ModuleType("atbash_hermes_plugin_more")
|
|
27
20
|
_plugin.__file__ = str(_MODULE_PATH)
|
{atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_zero_bugs_round.py
RENAMED
|
@@ -19,16 +19,6 @@ from pathlib import Path
|
|
|
19
19
|
|
|
20
20
|
_ATBASH_AVAILABLE = importlib.util.find_spec("atbash") is not None
|
|
21
21
|
|
|
22
|
-
# Pass-through stub so unit tests can exercise memory-write paths without the
|
|
23
|
-
# real atbash-sdk installed. The SDK tests that need the real package are
|
|
24
|
-
# guarded with @skipUnless(_ATBASH_AVAILABLE, ...) below.
|
|
25
|
-
if "atbash_core" not in sys.modules:
|
|
26
|
-
_stub = types.ModuleType("atbash_core")
|
|
27
|
-
_stub.redact_secrets = lambda text: types.SimpleNamespace(redacted=text)
|
|
28
|
-
_stub.create_memory_snapshot = lambda entries: None
|
|
29
|
-
_stub.diff_memory_snapshots = lambda *a, **k: None
|
|
30
|
-
sys.modules["atbash_core"] = _stub
|
|
31
|
-
|
|
32
22
|
_MODULE_PATH = Path(__file__).resolve().parents[1] / "atbash_hermes_plugin" / "__init__.py"
|
|
33
23
|
_plugin = types.ModuleType("atbash_hermes_plugin_zero_bugs")
|
|
34
24
|
_plugin.__file__ = str(_MODULE_PATH)
|
|
@@ -242,8 +232,8 @@ class SdkCapabilityAgainstRealSdk(unittest.TestCase):
|
|
|
242
232
|
|
|
243
233
|
version = _v("atbash-sdk")
|
|
244
234
|
self.assertTrue(
|
|
245
|
-
version.startswith("0.
|
|
246
|
-
f"expected the current SDK line 0.
|
|
235
|
+
version.startswith("0.15"),
|
|
236
|
+
f"expected the current SDK line 0.15.x, got {version}",
|
|
247
237
|
)
|
|
248
238
|
_plugin._assert_sdk_capabilities(atbash, Atbash, ToolCallInput)
|
|
249
239
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/atbash_hermes_plugin/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_guard_selfprotection.py
RENAMED
|
File without changes
|
|
File without changes
|
{atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_release_contract.py
RENAMED
|
File without changes
|
{atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_zero_bugs_fuzz.py
RENAMED
|
File without changes
|