Metadata-Version: 2.4
Name: sentience-governor
Version: 0.2.2
Summary: Open-tier governance wrapper for enterprise AI agents
Author-email: "Crescere Labs, Inc." <ossgov@crescerelabs.ai>
License-Expression: Apache-2.0
Project-URL: Homepage, https://getsentience.ai
Keywords: ai,agent,governance,observability,policy,llm,mcp,langchain,trace,audit
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Security
Classifier: Topic :: System :: Monitoring
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pydantic>=2.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Provides-Extra: demo
Requires-Dist: anthropic>=0.40; extra == "demo"
Requires-Dist: pyairtable>=2.0; extra == "demo"
Requires-Dist: langchain-anthropic>=0.3; extra == "demo"
Requires-Dist: langchain-core>=0.3; extra == "demo"
Dynamic: license-file

# sentience-governor

See exactly what your AI agents did, why they did it, and where they crossed boundaries — in real time.

Install → run → see your first trace in under 2 minutes.

`sentience-governor` is the open-tier governance wrapper for AI agents. It intercepts agent tool calls at the execution boundary, evaluates them against a default policy set, and produces a structured local trace.

It does this without modifying agent behavior, without blocking execution, and without sending anything anywhere automatically.

> **Status:** v0.2.2. Apache 2.0 licensed. Runtime + Sentience Sync CLI + live production sync endpoint at `https://sync.getsentience.ai/v1`. Full test suite (385 tests) + golden-trace coverage + per-commit acceptance gate against live prod.

## Simple mental model

Session = one run of an agent
Event = one action inside a session
Trace = all events in a session

---

## What it does

- Captures agent tool calls at the execution boundary
- Emits structured governance events
- Evaluates each event against five default policy rules
- Produces local traces you can inspect
- Shows what the paid control plane would have done, without blocking anything

---

## What it does not do

- Does not block or modify agent execution in the open tier
- Does not send telemetry, licensing checks, or data automatically
- Does not persist data across sessions
- Does not require a Sentience account, API key, or network connection
- Does not classify data unless classification is provided by the integration

---

## Install

Requires Python 3.10+.

```bash
pipx install sentience-governor
```

That installs the four CLIs (`sentience`, `sentience-cli`, `sentience-sync`, `sentience-claude-code-hook`) into an isolated environment and exposes them on your `$PATH`.

If you don't have `pipx`: `brew install pipx` on macOS, or `python3 -m pip install --user pipx` on Linux. Then `pipx ensurepath` and restart your shell.

If you're integrating Sentience as a library inside your own virtualenv (MCP wrapper, LangChain callback, or your own Python code), `pip install sentience-governor` works fine inside an active venv.

No account. No signup. No API key.

---

## Start here: first trace

If you use Claude Code, this is the fastest path.

1. Install:

   ```bash
   pipx install sentience-governor
   ```

2. Add the Claude Code hook config shown in the [Quickstart (Claude Code hook)](#quickstart-claude-code-hook) section below.

3. Run Claude Code normally.

4. Review the latest trace:

   ```bash
   sentience open --latest --summary
   ```

You should see:

- A session listed
- Events captured
- A summary explaining what happened

If you see nothing, run:

```bash
sentience status
sentience list
```

---

## Commands

### Most used commands

```bash
sentience open --latest --summary   # see what happened
sentience list                      # see recent sessions
sentience status                    # confirm the hook is capturing
sentience-sync run                  # optional: upload aggregated counts
```

Installing the package registers four CLI entry points. Skim this table before reading further; pick the one that matches what you're doing.

| Command | Use it when… | Details |
| :-- | :-- | :-- |
| **`sentience`** | reviewing an agent-hook session trace (Claude Code today; more coming). Curated, signal-first output. | `sentience status` / `sentience list` / `sentience open [--latest \| <id>] [--summary]` |
| **`sentience-cli`** | inspecting a library trace (MCP wrapper / LangChain handler) or a golden-trace fixture. Raw, full-fidelity. | `sentience-cli <file>` / `my-agent \| sentience-cli` |
| **`sentience-claude-code-hook`** | invoked by Claude Code via `.claude/settings.json`. Not run directly by operators. | Reads JSON on stdin, emits governance events, exits 0. |
| **`sentience-sync`** | opting in to send aggregated rule-fire counts to Sentience Cloud. Manual, explicit, one run at a time — never automatic. Requires `--email` and `--name` on first `register`. | `sentience-sync init` / `register --email ... --name ...` / `run` / `status` / `update-check` |

**`sentience` subcommands (agent-hook viewer):**

| Subcommand | What it does |
| :-- | :-- |
| `sentience status` | Is the hook capturing sessions? Prints the trace path and last session. |
| `sentience list` | One line per session, newest first, max 20. |
| `sentience open --latest` | Full curated render of the most recent session (Summary / Focus / Notes / Key Events / Full Trace / Footer). |
| `sentience open --latest --summary` | Same render, minus the Full Trace block — fits one terminal screen. |
| `sentience open <session_id>` | Render a specific session by id or prefix. |

**Two-CLI discipline:** `sentience-cli` and `sentience` exist on purpose. They serve different audiences (library integrators vs agent-hook operators) with different trace shapes (10–50 events vs hundreds).

---

## Quickstart (Claude Code hook)

If you're using Claude Code, governance is a four-line settings block — no code changes. Drop this into `.claude/settings.json` (project-local) or `~/.claude/settings.json` (user-global):

```json
{
  "hooks": {
    "PreToolUse": [
      {"matcher": "", "hooks": [{"type": "command", "command": "sentience-claude-code-hook"}]}
    ],
    "PostToolUse": [
      {"matcher": "", "hooks": [{"type": "command", "command": "sentience-claude-code-hook"}]}
    ]
  }
}
```

Every Claude Code tool call now emits a governance event. Per-session traces land at `~/.sentience/traces/claude-code/<session_id>.jsonl` by default. Fail-open, observe-only, no credentials.

Known blind spot: Bash commands are captured at the tool-call boundary but the shell command string itself is not parsed (the hook sees `Bash(cmd='...')`, not what the shell ultimately resolves).

Review what Claude Code actually did with the curated `sentience` CLI:

```bash
sentience status                    # did the hook fire? yes / no
sentience list                      # what sessions exist
sentience open --latest --summary   # curated view of the latest session, one screen
```

This is the main first-value path for operators.

---

## Quickstart (MCP client wrapper)

`wrap_mcp_client()` targets an internal `MCPClientLike` protocol. To wrap a concrete MCP SDK client, adapt it through `SentienceMCPAdapter` — the adapter keeps the wrapper free of any SDK-specific method names.

```python
from sentience_governor.cache.cache import InProcessCache
from sentience_governor.session_manager.manager import SessionManager
from sentience_governor.sink.writer import SinkWriter, StdoutSink
from sentience_governor.wrapper.mcp import (
    SentienceMCPAdapter,
    wrap_mcp_client,
)

# Shared per-process collaborators.
session_manager = SessionManager()
cache = InProcessCache()
sink = SinkWriter(StdoutSink())

# Adapt your SDK client to the MCPClientLike protocol.
# `call_fn(delegate, tool_name, arguments) -> dict` is the only SDK-aware bit.
adapted = SentienceMCPAdapter(
    delegate=your_sdk_client,
    call_fn=lambda client, name, args: client.call_tool(name, args),
)

wrapped = wrap_mcp_client(
    target=adapted,
    session_manager=session_manager,
    cache=cache,
    sink_writer=sink,
    agent_id="my-agent",
    agent_version="1.0.0",
    vendor_id="my-company",
    declared_capabilities=["crm.read"],
    owner_claim="user_123",
    stated_objective="Generate Q1 customer report",
)

async with wrapped:
    result = wrapped.send_tool_call("crm.get_customer", {"id": "123"})
```

Integration consists of three steps: import Sentience, assemble the collaborators, wrap the client.

---

## Quickstart (LangChain-based agent)

Attach `SentienceCallbackHandler` as a callback. It duck-types the LangChain `BaseCallbackHandler` interface, so no hard dependency on `langchain-core` at import time.

```python
from sentience_governor.cache.cache import InProcessCache
from sentience_governor.session_manager.manager import SessionManager
from sentience_governor.sink.writer import SinkWriter, StdoutSink
from sentience_governor.wrapper.langchain_adapter import SentienceCallbackHandler

session_manager = SessionManager()
cache = InProcessCache()
sink = SinkWriter(StdoutSink())

handler = SentienceCallbackHandler(
    agent_id="my-agent",
    session_manager=session_manager,
    cache=cache,
    sink_writer=sink,
    agent_version="1.0.0",
    declared_capabilities=["crm.read"],
    owner_claim="user_123",
)

agent.invoke(
    {"input": "Generate Q1 customer report"},
    config={"callbacks": [handler]},
)
```

If you are building with `create_react_agent` and prefer the middleware integration path, `SentienceMiddleware` wraps the same handler and plugs in via the `middleware=` keyword. It is observe-only in v0 — it never blocks.

```python
from sentience_governor.wrapper.langchain_adapter import SentienceMiddleware

middleware = SentienceMiddleware(handler)
agent = create_react_agent(llm=llm, tools=tools, prompt=prompt, middleware=[middleware])
```

---

## Control points

`sentience-governor` wraps your agent's execution boundary and emits structured governance events at five control points:

- **AGENT_REGISTERED** — who is acting
- **INTENT_DECLARED** — what they are trying to do
- **SCOPE_ASSERTED** — what tool they are calling and why
- **CONTEXT_SNAPSHOT** — what data is in their context
- **MEMORY_WRITE_ATTEMPT** — what they are trying to persist

A sixth event type, **GOVERNANCE_ERROR**, is emitted by the runtime itself when an internal fault occurs (sink unavailable, schema violation, intercept failure, timeout). It is always routed to stdout regardless of the configured sink, and never interrupts the agent.

---

## View your trace

For Claude Code sessions:

```bash
sentience open --latest --summary
```

For raw library traces:

```bash
sentience-cli path/to/agent.jsonl
```

Use `sentience` for high-volume agent-hook sessions. Use `sentience-cli` for full-fidelity library traces.

---

## Default policy rules

| Rule    | What it checks                                                                 |
| :------ | :----------------------------------------------------------------------------- |
| POL-001 | Agent must declare intent before executing mutating operations                 |
| POL-002 | Agents must be registered before accessing tools                               |
| POL-003 | Data entering context must be classified                                      |
| POL-004 | Memory writes must carry classification and retention policy                  |
| POL-005 | Sensitive data must not escalate in context without explicit authorization     |

All five rules are evaluated on every session. Violations are surfaced and simulated — never enforced in the open tier.

---

## Sinks

Three sinks are shipped. Pick one at construction time and pass it to `SinkWriter`.

| Sink            | Use                                                                              |
| :-------------- | :------------------------------------------------------------------------------- |
| `StdoutSink`    | Default. One JSON object per line to stdout. Pipe into `sentience-cli`.          |
| `FileSink`      | Append newline-delimited JSON to a file path.                                    |
| `HttpLocalSink` | POST each event as JSON to a local URL. 500 ms timeout, no retry. Loopback only. |

Failure semantics are uniform across sinks:

- Writes are synchronous, unbuffered, and never retried.
- On failure, the event is dropped and a `GOVERNANCE_ERROR` is routed to stdout.
- Severity escalates per session: 1 failure → `warning`, 3 consecutive → `degraded`, remainder of the session → `critical` on close.
- The agent is never blocked.

---

## Sentience Sync

Optional. Manual. Sends only aggregated counts — never raw events, tool arguments, tool responses, or payloads.

Sentience Sync is the opt-in feedback channel between your installation and Sentience Cloud. It is how both sides learn:

- **For you (the operator):** is my policy working? are my agents actually hitting the rules I care about? is my installation running the version I think it is?
- **For Sentience (the maintainer):** which policies fire in the wild, at what frequency, across which runtime versions? Signals that inform the next release.

It is **completely opt-in**. Installing the runtime sends nothing. Wrapping an agent sends nothing. Events land in local trace files on your machine only. Sync transmits anything to Sentience Cloud **only when you explicitly run `sentience-sync register` and then `sentience-sync run`**.

### Why it exists

Sync gives operators a lightweight roll-up of policy activity and update notices for their installed runtime version. It also gives maintainers aggregate signal about which rules fire in real deployments.

It never auto-applies updates and never changes runtime behavior.

### What Sentience Cloud sees

Every `sentience-sync run` POST contains only:

- `installation_id`
- `runtime_version`
- `window_start` / `window_end`
- `counts_by_rule`

Sentience Cloud never sees raw event content, agent IDs, user IDs, tool names, arguments, responses, payloads, source code, logs, or business context.

Registration additionally collects email and name because Sync is the update/support channel. `organization` and `operator_role` are optional.

### Safety

Built for unattended runs. Failure is safe.

- No state advancement on failed uploads — failed runs retry from the same offset, no data loss.
- Logs are always the source of truth.
- Duplicate runs are idempotent (server returns `duplicate=true`, CLI exits 0).

### CLI

```
sentience-sync init
sentience-sync register --email you@example.com --name "Your Name"
sentience-sync run            # default
sentience-sync status
sentience-sync aggregate
sentience-sync update-check
```

Exit codes: `0` success · `1` config · `2` network · `3` not registered · `4` server · `5` state.

### Config

```json
{
  "endpoint": "https://sync.getsentience.ai/v1",
  "log_sources": ["/var/log/sentience/agent.jsonl"],
  "report_geo": false
}
```

Precedence: CLI > env > file > defaults.

The default endpoint is live. Use `SENTIENCE_SYNC_ENDPOINT` or the `endpoint` field in your config to point at a different target.

### Boundary

- Runtime has zero dependency on Sync — enforced in tests across the codebase.
- Sync cannot influence execution or policy.
- Uninstalling Sync (deleting the `sentience_sync` directory) does not affect the runtime.

---

## Upgrade to enforcement

The open tier answers: *what happened*. The paid control plane adds enforcement: *what should be blocked, narrowed, paused, or escalated*.

Real enforcement — block, narrow, scope contraction, pause — and organisational memory across sessions and agents are paid control-plane capabilities.

---

## License

Apache 2.0. See <https://www.apache.org/licenses/LICENSE-2.0>.

Copyright 2026 Crescere Labs, Inc.

---

*Sentience — Crescere Labs, Inc.*
