Metadata-Version: 2.4
Name: sentience-governor
Version: 0.2.8.2
Summary: Local-first governance for AI agents: structured execution traces with intent drift, policy violations, and per-turn token burn — read from the CLI or inside Claude Code.
Author-email: "Crescere Labs, Inc." <ossgov@crescerelabs.ai>
License-Expression: Apache-2.0
Project-URL: Homepage, https://getsentience.ai
Project-URL: Documentation, https://getsentience.ai/docs/
Project-URL: Changelog, https://getsentience.ai/docs/changelog/
Keywords: ai,agent,governance,observability,policy,llm,mcp,langchain,claude-code,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
Requires-Dist: PyYAML>=6.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

Local-first governance for AI agents. Connect Claude Code, run `sentience pulse`, and get a shareable report of agent actions, intent drift, policy violations, and per-turn token burn.

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

`sentience-governor` is the open-tier governance wrapper for AI agents. It intercepts execution at the boundary, observes it against governance primitives and default policy rules, and produces a structured local trace — now with optional per-turn token-burn attribution so you can correlate token/context burn with execution behavior.

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

## What's new in 0.2.8.2

**Reports that explain themselves.** v0.2.8 stopped the chat surface
from guessing; v0.2.8.2 removes the reason it had to — the measured
output now carries its own meaning.

- **The four token classes, in the pulse.** `sentience pulse` breaks
  total compute into cached read / cached write / prompt / completion —
  the most telling fact about a Claude Code session (cache reads usually
  dominate, often well over 90%), and the four reconcile exactly to the
  total. It also states how usage is counted ("per-turn usage is deduped
  by requestId") so cache-read totals aren't misread as cumulative.
- **The live pulse just works.** Resuming a Claude Code conversation
  mints a new session id, so the newest session is often an empty live
  segment. When that happens, `sentience pulse` now **shows your most
  recent session that *does* have token data** — with a transparent
  header naming it — instead of an empty report. An explicit `sentience
  pulse <id>` is always honoured exactly. (The `analyze` commands name
  the session in their empty state.)
- **`sentience status --json` always returns JSON**, even on empty
  states (no trace directory / no sessions yet).
- **Truer copy across the board** — READ operations are no longer
  described as blocked writes; `context_size_tokens` is documented as a
  per-snapshot size, not model usage; reserved profile fields read as
  reserved; the sync sign-up CTA points to
  `getsentience.ai/sentience-sync`.

Additive release in the v0.2.8 line — no `schema_version` bump, no new
event types; existing traces and tooling are unchanged.

## What was new in 0.2.8

**Claude Code skills for slash-command reporting.** Sentience now lives
inside the Claude Code chat. `sentience init claude-code` installs six
operator-invoked `/sentience-*` commands, so you can read governance
signals without leaving the conversation — no terminal context-switch.

```text
Install once:
  pipx install sentience-governor
  sentience init claude-code

Then restart Claude Code and type:
  /sentience-help
  /sentience-pulse
```

| Command | What it shows |
| :-- | :-- |
| `/sentience-help` | What the commands do and their boundaries |
| `/sentience-pulse` | One-command report for the latest captured session |
| `/sentience-status` | Is the hook capturing? |
| `/sentience-profile` | The active governance profile (view-only) |
| `/sentience-violations` | Per-rule policy-violation drill-down |
| `/sentience-intent` | Per-turn intent-drift drill-down |

Each command is a **thin wrapper over the CLI you already have** — the
CLI produces the deterministic numbers at preprocessing time, and Claude
renders them inline and is instructed to show them verbatim. Anything
Claude adds beyond the rendered report is interpretation, not Sentience
measurement. The surface is deliberately bounded:

- **Operator-invoked only.** Claude won't run these on its own; you
  type them. (Claude-initiated governance is a later MCP release.)
- **Latest session only.** No history browsing, search, or
  cross-session aggregation from the slash surface.
- **Read-only.** `/sentience-profile` is view-only; no slash command
  mutates your governance, session, or remote state.

`--no-skills` wires hooks without installing the commands. By default,
hooks are wired for the initialized project while skills install to your
personal `~/.claude/skills/`; pass `--project` to install skills into
that project's `.claude/skills/` instead. Sentience tracks the skill
files it installed and preserves hand-edited skills by default; pass
`--force` to replace them with the packaged version. This release also
adds **`sentience --version`**, corrects a first-install PATH check
that could print a spurious "not resolvable" warning, splits status
counts into **policy violations vs advisory flags** (with a
`status --json` reconciliation view), and reports **which rules fired**
even before a session ends and token attribution becomes available.
Backward-compatible additive release — existing traces, hooks, and
tooling are unchanged.

## What was new in 0.2.6.1

**Claude Code per-turn token capture.** A fast-follow that makes
`sentience pulse` actually work on a Claude Code session. Before this,
pulse reported `no_signal` there — the hook captured tool calls but no
per-turn token burn, so there was nothing to attribute. Now the hook
reads the session transcript at the end of the run and records the real
token burn of every model turn, including the cached context tokens that
dominate Claude Code usage (a turn can show 2 input tokens but read
~39,000 cached).

```bash
sentience init claude-code   # now also wires the SessionEnd hook
sentience pulse --latest      # real per-turn token burn, not no_signal
```

Each tool call is matched to the model turn that issued it **by id, not
by position**, so a policy violation lands on the right turn. Reports
separate total session token burn from the part attributable to a
tool-call violation, and disclose subagent burn as excluded when
Task/Agent activity is present. Token **burn** means token / context
footprint — never dollar cost. No schema change; existing traces and
tooling are unchanged. Capture is fail-open and idempotent.

## What was new in 0.2.6

**Sentience Pulse.** One command summarizes a session across every
analyzer we ship — undeclared-intent spend (v0.2.4), policy-
violation burn rate (new in v0.2.6), and an advisory-flag-
occurrence summary — plus a one-line "why it matters" translation
under each section.

```bash
sentience pulse --latest         # one-command session report
sentience pulse --latest --save  # save the Markdown report
sentience pulse --showcase       # bundled example, works on any install
```

Plus: the burn-rate analyzer is exposed standalone as `sentience
analyze policy-violations` for per-metric drill-down. Both surfaces
use association language ("appeared on turns representing N
tokens"), not savings or causality wording — the metric is a
prioritization signal for operator inspection, not a savings
estimate. Three pre-rendered showcase pulses ship under
`examples/showcase/v026-pulse/` (clean / missing-intent / mixed-
violations) for the three states an operator actually encounters.
No schema changes; additive CLI surface only.

## What was new in 0.2.5.5

**Fresh-install adoption.** The whole first journey now works without a walkthrough: install → discover commands → wire the hook → capture → inspect/analyze → author the profile.

```bash
sentience init claude-code              # wire the Claude Code hook — no hand-edited JSON
sentience                               # bare command now prints a helpful guide
sentience demo undeclared-intent        # a runnable example, works on any install
sentience analyze undeclared-intent --showcase   # populated analysis before token capture is wired
```

Plus: `sentience open` accepts a trace file path (not just a session id); `--latest` orders by session start time so a live session never reorders results; `sentience profile init` now ships inline-commented YAML you can read standalone; `sentience profile edit` falls back through `$VISUAL`/`$EDITOR`/`nano`/`vim`/`vi`/TextEdit. No schema changes; additive CLI surface only. (0.2.5.2–0.2.5.4 were consumed during TestPyPI validation — see CHANGELOG.)

## What was new in 0.2.5

**Operator-defined governance posture.** v0.2.5 introduces the first durable, operator-authored artifact the runtime evaluates against: the **governance profile** at `~/.sentience/profile.yaml`. One profile, evaluated across every wrapper surface — Claude Code hook, MCP wrapper, LangChain handler. Authored once; travels with the operator.

```bash
# Common commands (six verbs total — see the Commands table below for export/import)
sentience profile init       # create your first profile
sentience profile view       # inspect it
sentience profile edit       # tune it ($VISUAL/$EDITOR, else nano/vim/vi, else macOS TextEdit)
sentience profile validate   # schema check (read-only — never mutates the file)
```

The profile encodes three things the runtime asks of every governed session:

1. **When undeclared intent is surfaced** — per-event, first-write-only, or never.
2. **When the agent has crossed a task boundary** — directory change, file-type shift, read-to-write transition, or time gap.
3. **Which tools should be treated as high-consequence** — operator-authored regex patterns matched against `<tool_id>:<target_system>`.

All signals are observational; nothing is blocked. Every event in a governed session carries a 12-character `profile_fingerprint` so traces correlate back to the profile that produced them. Two new advisory flags — `TASK_BOUNDARY_CROSSED` and `HIGH_CONSEQUENCE_DETECTED` — fire when a profile match triggers. The `sentience analyze undeclared-intent` analyzer gains three optional report sections (Profile, High-consequence operations, Task boundaries crossed) that surface in both CLI and Markdown output.

**No breaking changes.** Sessions that run without a profile produce traces byte-identical to v0.2.4. The profile system is strictly additive: existing v0.2.4 integrations continue to work unchanged.

See `userdocs/sentience_governor.md` §11 for the full guide and `examples/showcase/v025-closed-loop/` for a complete runnable walkthrough — profile, agent recipe, generated trace, generated analyzer report.

## What was new in 0.2.4

**Undeclared-intent token spend.** First derived metric over the v0.2.3 token-attribution substrate. Tells you how much compute was attributed to reasoning turns that touched execution outside the session's declared operational intent.

```bash
sentience analyze undeclared-intent --latest
```

Deterministic analyzer with replay-stable output. Optional Markdown-report save flow. Differentiated copy when no intent was declared anywhere — frames the result as a surface-level limitation (e.g. Claude Code hooks today) rather than agent drift. See `userdocs/sentience_governor.md` §10 for the full guide and `examples/showcase/` for three pre-rendered scenarios.

## What was new in 0.2.3

**Execution-cost attribution.** Token spend now rides directly on execution-boundary traces. Per-turn identity (`llm_turn_id`) keeps multi-tool-call attribution mathematically correct under aggregation. Optional, all fields default `None`; non-adopters see zero schema change.

**First-run UX.** The CLI greets you the first time you run it and offers an optional one-line ask to hear when the hosted console ships. Easy to skip, never re-asks.

**LangChain + LangGraph.** `SentienceCallbackHandler.on_llm_start` / `on_llm_end` callbacks capture per-turn LLM token usage automatically. LangGraph users can register `SentienceMiddleware.awrap_step` for step-level aggregation.

## Simple mental model

Sentience governs at the execution boundary, not after the fact.

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 paid enforcement would have flagged, 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

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

That's the install. Two commands. Registers four CLIs on your `$PATH`: `sentience`, `sentience-cli`, `sentience-sync`, `sentience-claude-code-hook`.

**Verify install:**
```bash
pipx list
```

**Upgrade:**
```bash
pipx upgrade sentience-governor
```

**Uninstall:**
```bash
pipx uninstall sentience-governor
```

**If you don't have pipx:** `brew install pipx` (macOS) or `python3 -m pip install --user pipx` (Linux/WSL), then `pipx ensurepath` and restart your shell.

**Library integration:** if you're importing `sentience_governor` as a Python module inside your own project (MCP wrapper, LangChain callback, custom agent runtime), use `pip install sentience-governor` inside your project's virtualenv. The pipx path above is for CLI usage; pip-in-venv is for library usage.

Requires Python 3.10+. No account. No signup. No API key.

**Want to know when the hosted console ships?** Drop your email at https://getsentience.ai/launch-list — or just install and we'll ask politely on first run. Optional, easy to skip, never re-asks.

---

## Start here: first trace

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

1. Install:

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

2. Wire the Claude Code hook (one command — also installs the `SessionEnd` hook that captures per-turn token burn):

   ```bash
   sentience init claude-code
   ```

3. Run Claude Code normally.

4. Review the session — inside Claude Code (restart Claude Code once
   after the first install, then type):

   ```text
   /sentience-pulse
   ```

   Or from a terminal:

   ```bash
   sentience pulse --latest            # one-command report: intent drift, policy violations, token burn
   ```

   Optional, for an event-by-event inspection of the 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

Inside Claude Code:

```text
/sentience-help                 # what the commands do and their boundaries
/sentience-pulse                # one-command session report
/sentience-status               # confirm the hook is capturing
/sentience-profile              # view the active governance profile
/sentience-violations           # policy-violation drill-down
/sentience-intent               # intent-drift drill-down
```

From a terminal:

```bash
sentience pulse --latest            # one-command session report
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 init claude-code [path]` | Wire the Claude Code hook into a project's `.claude/settings.json` (idempotent merge) **and install the six `/sentience-*` slash commands**. Flags: `--no-skills` (hooks only), `--project` (project-local skills), `--force` (overwrite hand-edited skills). (v0.2.5.5+; skills v0.2.8+) |
| `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 \| path>` | Render a specific session by id/prefix **or** a trace file path. |
| `sentience pulse [--latest \| --showcase]` | **One-command session report** — composes undeclared-intent, policy-violation burn rate, and an advisory-flag summary, each with a "why it matters" line. `--json` / `--save` supported; all statuses are save-eligible. (v0.2.6+) |
| `sentience analyze undeclared-intent [--latest \| --showcase]` | Compute how much compute was attributed to turns that touched execution outside the session's declared intent. `--showcase` runs a bundled example. Add `--json` for structured output, `--save` to write a Markdown report. (v0.2.4+) |
| `sentience analyze policy-violations [--latest \| --showcase]` | Per-rule attribution of compute on turns where any of POL-001–POL-005 fired. Same flags as `undeclared-intent`; save-eligible on `ok` / `no_violations`. (v0.2.6+) |
| `sentience demo {undeclared-intent,closed-loop}` | Run a packaged demo session through the analyzer — works on any install. (v0.2.5.5+) |
| `sentience profile {init,view,validate,export,import,edit}` | Manage the governance profile at `~/.sentience/profile.yaml`. `init` creates a starter (now with inline-commented YAML), `view` inspects it, `validate` is a read-only schema check, `edit` opens an editor ($VISUAL/$EDITOR/nano/vim/vi/TextEdit). (v0.2.5+) |

**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, one command wires the hook into your project — no hand-edited JSON, no code changes:

```bash
sentience init claude-code        # wires the hook into ./.claude/settings.json
# or target a specific project:
sentience init claude-code /path/to/project
```

This writes (or idempotently merges into) `.claude/settings.json` with the correct absolute path to the hook binary for your install. It never clobbers existing hooks or settings; re-running is a no-op.

It also installs the six `/sentience-*` slash commands into `~/.claude/skills/` (v0.2.8+), so you can inspect governance from the Claude Code chat:

```bash
# After install, restart Claude Code and type:
/sentience-help     # what the commands do and their boundaries
/sentience-pulse    # one-command report for the latest captured session
```

The commands are operator-invoked only, latest-session only, and read-only. Pass `--no-skills` to wire hooks without them, `--project` to install them into the project's `.claude/skills/` (shareable via git), or `--force` to overwrite a skill you've hand-edited.

<details>
<summary>Prefer to wire it by hand?</summary>

Drop this into `.claude/settings.json` (project-local) or `~/.claude/settings.json` (user-global). Replace `sentience-claude-code-hook` with its absolute path if it isn't on Claude Code's `$PATH`. **All three hooks are required** — omit `SessionEnd` and per-turn token burn is never captured, so `sentience pulse` reports `no_signal`:

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

(`sentience init claude-code` wires all three for you.)

</details>

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])
```

**Token tracking (optional, v0.2.3+).** `SentienceCallbackHandler.on_llm_start` and `on_llm_end` capture per-turn LLM token usage from LangChain responses automatically and attach it to subsequent tool-call events with a per-turn `llm_turn_id` so multi-tool-call attribution stays mathematically correct. LangGraph users can additionally register `SentienceMiddleware.awrap_step` for step-level aggregation. See `userdocs/sentience_governor.md` "Token tracking (optional)" for the full integration guide, per-provider cache-token semantics, and the aggregation contract.

---

## 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*. Enforcement — *what should be blocked, narrowed, paused, or escalated* — is planned for a future paid tier.

Real enforcement (block, narrow, scope contraction, pause) and organisational memory across sessions and agents are on the roadmap, not available today.

---

## License

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

Copyright 2026 Crescere Labs, Inc.

---

*Sentience — Crescere Labs, Inc.*
