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.
Files changed (25) hide show
  1. {atbash_hermes_plugin-0.12.0.dev0/atbash_hermes_plugin.egg-info → atbash_hermes_plugin-0.15.0}/PKG-INFO +63 -5
  2. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/README.md +61 -3
  3. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/atbash_hermes_plugin/__init__.py +7 -21
  4. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0/atbash_hermes_plugin.egg-info}/PKG-INFO +63 -5
  5. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/atbash_hermes_plugin.egg-info/requires.txt +1 -1
  6. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/plugin.yaml +1 -1
  7. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/pyproject.toml +2 -2
  8. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_memory_poisoning.py +0 -7
  9. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_pre_tool_call_verdicts.py +1 -1
  10. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_zero_bugs_issues.py +0 -7
  11. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_zero_bugs_more.py +0 -7
  12. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_zero_bugs_round.py +2 -12
  13. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/LICENSE +0 -0
  14. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/MANIFEST.in +0 -0
  15. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/NOTICE +0 -0
  16. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/atbash_hermes_plugin/__main__.py +0 -0
  17. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/atbash_hermes_plugin.egg-info/SOURCES.txt +0 -0
  18. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/atbash_hermes_plugin.egg-info/dependency_links.txt +0 -0
  19. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/atbash_hermes_plugin.egg-info/entry_points.txt +0 -0
  20. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/atbash_hermes_plugin.egg-info/top_level.txt +0 -0
  21. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/setup.cfg +0 -0
  22. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_guard_selfprotection.py +0 -0
  23. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_memory_write_fail_closed.py +0 -0
  24. {atbash_hermes_plugin-0.12.0.dev0 → atbash_hermes_plugin-0.15.0}/tests/test_release_contract.py +0 -0
  25. {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.12.0.dev0
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.12.0.dev0
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 held for operator review. The agent will not be jailed — please approve or reject this request from the Atbash dashboard, then ask the agent to try again.
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 dashboard approval or rejection, the SDK can query held-action status with
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 review, rather than automatically re-running the action.
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 held for operator review. The agent will not be jailed — please approve or reject this request from the Atbash dashboard, then ask the agent to try again.
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 dashboard approval or rejection, the SDK can query held-action status with
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 review, rather than automatically re-running the action.
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
 
@@ -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 held for operator review. The agent will not be jailed — please approve "
30
- "or reject this request from the Atbash dashboard, then ask the agent to try "
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
- formatted = _redact_secrets(formatted).redacted # type: ignore
2340
+ from atbash_core import redact_secrets # type: ignore
2341
+ formatted = redact_secrets(formatted).redacted
2358
2342
  except Exception as _re:
2359
- logger.warning("Atbash memory write redaction failed err=%s", _re)
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.12.0.dev0
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.12.0.dev0
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 held for operator review. The agent will not be jailed — please approve or reject this request from the Atbash dashboard, then ask the agent to try again.
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 dashboard approval or rejection, the SDK can query held-action status with
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 review, rather than automatically re-running the action.
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
 
@@ -1,4 +1,4 @@
1
- atbash-sdk<1.0.0,>=0.12.0.dev0
1
+ atbash-sdk<1.0.0,>=0.15.0
2
2
  httpx<1,>=0.27
3
3
  opentelemetry-exporter-otlp-proto-http<2,>=1.29
4
4
  opentelemetry-sdk<2,>=1.29
@@ -1,5 +1,5 @@
1
1
  name: atbash-hermes-plugin
2
- version: 0.12.0.dev0
2
+ version: 0.15.0
3
3
  description: Atbash guardrail plugin for Hermes Agent (pre-tool-call policy enforcement)
4
4
  author: Chromia
5
5
  entry: atbash_hermes_plugin
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "atbash-hermes-plugin"
7
- version = "0.12.0.dev0"
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.12.0.dev0,<1.0.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",
@@ -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("held for operator review", result["message"].lower())
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"]:
@@ -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)
@@ -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)
@@ -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.12"),
246
- f"expected the current SDK line 0.12.x, got {version}",
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