Metadata-Version: 2.4
Name: creedspace-hermes
Version: 0.1.0
Summary: Creed Space constitutional values layer for Hermes Agent: per-turn constitution injection + an optional PDP tool gate.
Author: Creed Space
License: Apache-2.0
Project-URL: Homepage, https://creed.space
Project-URL: Source, https://github.com/Creed-Space/hermes-creedspace
Keywords: hermes-agent,openclaw,ai-safety,constitutional-ai,agent-guardrails,pdp
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Intended Audience :: Developers
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# Creed Space — a constitutional values layer for Hermes Agent

Hermes Agent ships strong execution sandboxing (command approval, container
isolation, credential stripping, untrusted-tool-result wrapping) but **no
values/constitution layer**. This plugin adds one.

- **`pre_llm_call` injection.** Every turn, the active Creed Space constitution
  is appended to the user message. Because it rides the user turn (not SOUL.md),
  it sidesteps Hermes' SOUL.md truncation *and* its context-file injection
  scanner entirely.
- **`tool_execution` PDP gate (optional).** Before each tool call the plugin can
  consult a local Creed PDP (`creed_decide`): `advisory` logs the verdict,
  `enforce` blocks a `DENY`.

> Positioning: *Hermes gives your agent power. Creed Space gives it principles.*

## Install

The standalone repo is **published** at
[`Creed-Space/hermes-creedspace`](https://github.com/Creed-Space/hermes-creedspace)
(Apache-2.0), so these resolve (a clean `git clone` + `pip install` is verified):

```bash
# 1. From the published repo (registry one-liner):
hermes plugins install creed-space/hermes-creedspace --enable

# 2. From the published repo via pip (entry-point discovery):
pip install git+https://github.com/Creed-Space/hermes-creedspace.git
hermes plugins enable creedspace
```

Also works from a clone of this monorepo (directory-plugin copy, or
`pip install ./integrations/hermes/creedspace`).

> **PyPI (`pip install creedspace-hermes`) is not yet published** — use the GitHub
> install above until it lands.

## Configure (environment variables)

| Variable | Default | Meaning |
|---|---|---|
| `CREEDSPACE_CONSTITUTION_PATH` | bundled `creed_default.md` | Path to a Creed constitution `.md` (frontmatter + Markdown body). |
| `CREEDSPACE_INJECT_MODE` | `full` | `full` injects the whole constitution body (default: the agent must see the actual rules, not just headings; full injection only happens on turn 1, reminders after). `summary` injects section headings only — an opt-in for the most compact possible injection. |
| `CREEDSPACE_INJECT_EVERY_TURN` | `1` | `1`: full on turn 1, a compact reminder every turn after. `0`: inject once per session. |
| `CREEDSPACE_PDP_MODE` | `off` | `off` \| `advisory` \| `enforce`. |
| `CREEDSPACE_PDP_BACKEND` | `mcp-stdio` | `mcp-stdio` (local co-located server) or `http` (hosted/remote endpoint). |
| `CREEDSPACE_PDP_COMMAND` | — | `mcp-stdio` backend: shell command launching the `creed-pdp` MCP stdio server (e.g. `services/mcp/run_pdp_server.sh`). Required when mode ≠ `off` and backend is `mcp-stdio`. |
| `CREEDSPACE_PDP_HTTP_URL` | — | `http` backend: the `POST /v1/decide` URL. Required when mode ≠ `off` and backend is `http`. Must be `https` for a remote host (a plaintext `http` URL is accepted only for loopback; a remote `http` URL is rejected so the bearer key is never sent in cleartext, and the gate then fails closed in enforce). |
| `CREEDSPACE_PDP_API_KEY` | — | `http` backend: bearer API key (`crd_live_…`). |
| `CREEDSPACE_CONSTITUTION_ID` | `default` | Registry id passed to `creed_decide` (mcp-stdio backend; the http backend binds the constitution to the API key server-side). |
| `CREEDSPACE_PDP_TIMEOUT_S` | `8.0` | Per-decision PDP timeout. |

**Two PDP backends, same gate.** `mcp-stdio` gates through a local co-located
`creed-pdp` server (offline, zero deploy) — the beachhead default. `http` gates
through a hosted Creed governance endpoint (`POST /v1/decide`, SDK contract), so the
same plugin works for agents that are not co-located with the repo. Both fail
*closed* in `enforce` mode: a `DENY`/`REQUIRE_HUMAN`, an unreachable server, or any
non-2xx HTTP response blocks the tool. A misconfigured backend (mode ≠ `off` but the
required command/URL unset) also blocks in `enforce` (`pdp_unconfigured`).

## Hosted (`http`) backend: URL, auth, fail-mode, latency

- **URL.** Point `CREEDSPACE_PDP_HTTP_URL` at the SDK decide path. That path takes the flat
  `{tool_name, arguments}` body this client sends and returns `ALLOW` (with a signed
  decision token) / `DENY` / `REQUIRE_HUMAN`. Must be `https` for a remote host (a plaintext
  remote URL is rejected so the bearer key is never sent in cleartext). The client
  **follows same-origin redirects** (so the `/v1/sdk/decide` alias, which 307-redirects to
  a canonical same-host path on staging, works when you point at it directly) but
  **refuses a cross-origin redirect**, so the bearer key never leaves the host you configured.
- **Auth.** `CREEDSPACE_PDP_API_KEY` is a **gateway** key (`crd_live_…` / `crd_test_…`),
  sent as `Authorization: Bearer`. It is *not* an SSaaS `cs_live_` key: those
  authenticate a different surface and will `401` here.
- **Fail-mode.** `enforce` fails **closed**: a `DENY`/`REQUIRE_HUMAN`, an unreachable
  endpoint, any non-2xx, a redirect (refused, so the bearer key is never resent), or any
  unexpected error all block the tool. `advisory` fails **open** (logs only). Choose
  `advisory` vs `enforce` per your risk tolerance.
- **Latency.** The gate is **synchronous** per tool call, bounded by
  `CREEDSPACE_PDP_TIMEOUT_S` (default `8.0`; on timeout the gate fails closed in
  enforce). A hosted decision runs a policy evaluation and can exceed 200ms, so the
  recommended shape is `enforce` on sensitive/irreversible tools and `advisory` (or
  `off`) on routine reads. Measure your real budget with the endpoint verification (see
  the remote-agent runbook) before enabling enforce fleet-wide.

The bundled constitution is a compact agentic-safety default; point
`CREEDSPACE_CONSTITUTION_PATH` at your own to encode your values.

## How it maps onto Hermes (verified against Hermes `main`, 2026-07-10)

- `pre_llm_call` callbacks receive `session_id, user_message, conversation_history,
  is_first_turn, model, platform, …` and may return `{"context": "<text>"}`; Hermes
  joins returns and appends them to the current turn's user message
  (`agent/turn_context.py`). Oversized context is spilled to disk, so injection is
  kept compact.
- `tool_execution` middleware wraps the real tool dispatch (`ctx.register_middleware`,
  mirrored on Hermes' own NeMo plugin). Returning a string in place of calling
  `next_call` substitutes the tool result — that is how `enforce` blocks.

## Safety / threat model

- **Truncation detection.** Each injection carries a footer with the rule count and
  an 8-char digest, so a truncated constitution is *detectable*, not silently partial.
- **Scanner-clean.** Hermes blocks a whole SOUL.md/AGENTS.md file if it contains
  verbatim jailbreak phrases (e.g. "ignore previous instructions"), even as things to
  resist. The bundled constitution avoids them; `_constitution.scan_risk_phrases()`
  lets template authors check. The `pre_llm_call` path is not scanned, so it is the
  robust delivery route for adversarial-sounding rules.
- **No standing grants.** Constitution templates express principles and gates, never
  pre-authorized entities/URLs — so the values layer cannot become a
  configuration-backdoor (identity-poisoning) vector.
- **Fail direction.** Injection and `advisory` fail *open* (inject/block nothing on
  error). `enforce` fails *closed* — an unreachable PDP blocks the tool.

## Tests / proof

- `tests/integrations/hermes/test_creedspace_plugin_contract.py` — deterministic
  contract tests (fake `PluginContext`, fake PDP client).
- `tests/integrations/hermes/test_creedspace_plugin_runtime.py` — loads the plugin
  through Hermes' own `PluginManager` and dispatches the real `pre_llm_call`. Skips
  unless Hermes is importable; run with
  `PYTHONPATH=/path/to/hermes-agent pytest …`. Verified passing against Hermes `main`
  on 2026-07-10 (the constitution reached the turn context under Hermes' own dispatch).
