Metadata-Version: 2.4
Name: memstrata-agent-hooks
Version: 0.2.0
Summary: One SDK for MemStrata conversational memory and agent governance hooks
Author: Called It Inc.
Maintainer-email: "Called It Inc." <support@memstrata.dev>
License-Expression: LicenseRef-Proprietary
Project-URL: Homepage, https://memstrata.dev
Project-URL: Documentation, https://memstrata.dev/docs
Project-URL: Issues, https://github.com/yadu9989/memstrata-agent-hooks/issues
Project-URL: Repository, https://github.com/yadu9989/memstrata-agent-hooks
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
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: Programming Language :: Python :: Implementation :: CPython
Requires-Python: <3.14,>=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: build<2,>=1.2; extra == "dev"
Requires-Dist: Cython<3.4,>=3.1; extra == "dev"
Requires-Dist: mypy<2,>=1.11; extra == "dev"
Requires-Dist: pytest<10,>=8; extra == "dev"
Requires-Dist: pytest-asyncio<2,>=0.24; extra == "dev"
Requires-Dist: ruff<1,>=0.6; extra == "dev"
Requires-Dist: setuptools<85,>=77; extra == "dev"
Requires-Dist: twine<7,>=6; extra == "dev"
Requires-Dist: wheel<1,>=0.45; extra == "dev"
Provides-Extra: crewai
Requires-Dist: crewai<2,>=1.14.7; extra == "crewai"
Provides-Extra: langchain
Requires-Dist: langchain-core<2,>=1.0; extra == "langchain"
Provides-Extra: langgraph
Requires-Dist: langgraph<2,>=1.0; extra == "langgraph"
Provides-Extra: frameworks
Requires-Dist: crewai<2,>=1.14.7; extra == "frameworks"
Requires-Dist: langchain-core<2,>=1.0; extra == "frameworks"
Requires-Dist: langgraph<2,>=1.0; extra == "frameworks"
Provides-Extra: test
Requires-Dist: pytest<10,>=8; extra == "test"
Requires-Dist: pytest-asyncio<2,>=0.24; extra == "test"
Dynamic: license-file

# MemStrata Agent Hooks SDK

This is one small SDK for two separate product jobs:

1. Conversational retrieves a bounded memory context and stores completed turns.
2. Governance checks tool calls and records their outcomes.

LangChain, LangGraph, CrewAI, OpenClaw, NeoClaw, Hermes, and custom agents call
the same local product services through this package. A framework-defined exact
key-value contract, such as LangGraph `BaseStore`, still uses an authoritative
framework backing store rather than pretending the compiled context endpoint
supports exact lookup or deletion.

## Install

Install the wheel matching your CPython version and operating system:

```bash
python -m pip install memstrata-agent-hooks
```

Optional framework dependencies remain separate:

```bash
python -m pip install "memstrata-agent-hooks[langchain]"
python -m pip install "memstrata-agent-hooks[langgraph]"
python -m pip install "memstrata-agent-hooks[crewai]"
```

Released wheels compile the proprietary transport, validation, facade, and
generic adapter implementation into native extensions. They contain no Python
source for those modules, and the project does not publish a source
distribution. Small framework adapters and host bundles remain readable
because LangChain, LangGraph, CrewAI, OpenClaw, Hermes, OpenCode, and NeoClaw
load that interoperability glue directly. The memory engine and Governance
policy implementation remain inside their separately licensed local products.

## Install for development

Python 3.10 through 3.13 is supported. The current CrewAI dependency stack is
not compatible with Python 3.14, so this release fails installation there
instead of leaving users with a broken optional adapter.

Release wheels target Windows x86-64, Linux glibc x86-64, and macOS 15 Apple
silicon. macOS Intel, Linux arm64, and Alpine Linux are not part of this first
release matrix.

```bash
python -m pip install -e sdk/agent-hooks
```

## Configure

```bash
export MEMSTRATA_MEMORY_URL=http://127.0.0.1:9101
export MEMSTRATA_GOVERNANCE_URL=http://127.0.0.1:9003
export MEMSTRATA_PROJECT=my-workspace
export MEMSTRATA_GOVERNANCE_SYSTEM_ID=my-enrolled-agent-system
export MEMSTRATA_GOVERNANCE_AGENT_ID=my-agent
export MEMSTRATA_GOVERNANCE_ACTOR='Support automation'
export MEMSTRATA_GOVERNANCE_PURPOSE='the exact purpose enrolled in Governance'
# Optional when the two products use separate credentials:
export MEMSTRATA_MEMORY_TOKEN='memory service token'
export MEMSTRATA_GOVERNANCE_TOKEN='governance service token'
```

On PowerShell, replace `export NAME=value` with `$env:NAME='value'`.
Both product-specific token variables fall back to `MEMSTRATA_TOKEN`. Tokens are
read from the environment and are never written into copied host configuration
files.

## Use one SDK object

`MemStrataAgentSDK` exposes the two product contracts without mixing them:

```python
from memstrata_agent_hooks import MemStrataAgentSDK

sdk = MemStrataAgentSDK.from_env()

sdk.memory.add_exchange(
    "Which plan should we use?",
    "The customer selected annual billing.",
    session_id="support-42",
)
context = sdk.memory.retrieve("Which billing plan did the customer select?")

with sdk.governance.guard(
    "send_email",
    {"to": "customer@example.com"},
    agent_id="support-agent",
):
    send_email()
```

The guard checks Governance before the side effect and records completion or
failure afterward using the same trace ID. If a receipt cannot be recorded
after the tool already ran, the SDK warns but does not make the host retry the
side effect.

`AsyncMemStrataAgentSDK` provides the same memory and Governance surfaces for
async hosts. The base package stays dependency-free and runs blocking HTTP in
worker threads.

## Use the low-level client

```python
from memstrata_agent_hooks import MemStrataClient

client = MemStrataClient.from_env()
client.remember("The customer chose annual billing", session_id="support-42")
context = client.context("What billing plan did the customer choose?")

decision = client.gate(
    "send_email",
    agent_id="support-agent",
    tool_input={"to": "customer@example.com"},
)
decision.require()
```

`gate` uses `POST /v1/pipeline/gate` and falls back to the compatible hook
endpoint. If Governance is unreachable, the default response is a visible
degraded allow decision only for a connection failure. This prevents an optional local daemon from freezing
an agent. Administrators can set `MEMSTRATA_GOVERNANCE_FAIL_CLOSED=1` for a
workflow where an unavailable policy service must stop execution.

Authentication, license, schema, policy, and server responses remain visible
errors. Only an explicit `allow` runs a tool. `review` and `block` both stop the
generic adapter so a host can route review to its own human approval surface.

## Framework adapters

The package root stays dependency-free. Install only the framework surfaces an
application uses:

```bash
python -m pip install "memstrata-agent-hooks[langchain]"
python -m pip install "memstrata-agent-hooks[langgraph]"
python -m pip install "memstrata-agent-hooks[crewai]"
```

### LangChain

`MemStrataRetriever` follows the current `BaseRetriever` Runnable contract. It
returns the complete bounded context pack as one `Document` and supports both
`invoke` and `ainvoke`:

```python
from memstrata_agent_hooks import MemStrataClient
from memstrata_agent_hooks.langchain import MemStrataRetriever

retriever = MemStrataRetriever(client=MemStrataClient.from_env())
documents = retriever.invoke("What changed in the release plan?")
```

`MemStrataConversationCallback` and
`AsyncMemStrataConversationCallback` store the current human message and the
completed answer. They do not re-ingest the system prompt or replayed history.
Set the callback metadata key `memstrata_session_id` when a workflow needs a
session ID other than the configured default.

`MemStrataGovernanceCallback` and `AsyncMemStrataGovernanceCallback` run a
Governance decision at `on_tool_start`, before the tool body, then correlate
`on_tool_end` or `on_tool_error` with the same trace ID. The handler sets
LangChain's `raise_error` flag so `review` and `block` decisions stop execution
instead of becoming callback warnings:

```python
from memstrata_agent_hooks.langchain import MemStrataGovernanceCallback

governance = MemStrataGovernanceCallback(
    MemStrataClient.from_env(), agent_id="support-agent"
)
answer = tool.invoke(tool_input, config={"callbacks": [governance]})
```

Register this callback where the host actually dispatches tools. Attaching it
only to a model call does not govern side effects performed elsewhere.

### LangGraph

`MemStrataLangGraphStore` is a real current `BaseStore` decorator. It delegates
exact get, search, put, delete, namespace listing, filtering, pagination, TTL,
batch, and async behavior to an authoritative LangGraph backing store:

```python
from langgraph.store.memory import InMemoryStore
from memstrata_agent_hooks import MemStrataClient
from memstrata_agent_hooks.langgraph import MemStrataLangGraphStore

store = MemStrataLangGraphStore(
    MemStrataClient.from_env(),
    InMemoryStore(),
)
```

For production, use a persistent LangGraph backing store. Conversational's SDK
API does not expose exact key lookup, filtered listing, or item-scoped erase,
so the adapter does not invent those semantics. Optional put mirroring must be
enabled explicitly with `mirror_mode="strict"` or `"best_effort"`. Mirrored
deletion also requires an administrator-approved `delete_mirror` callback.
Strict mirroring makes errors visible, but the backing-store write and
Conversational ingest are not one atomic transaction.

Namespaces organize a LangGraph store. They are not an authorization or tenant
boundary. Use separate MemStrata projects and credentials where isolation is
required.

The tested upstream contracts and exact remaining boundaries are recorded in
[`FRAMEWORK_CONTRACTS.md`](FRAMEWORK_CONTRACTS.md).

### CrewAI

Install the adapter once before a crew starts:

```python
from memstrata_agent_hooks import MemStrataClient
from memstrata_agent_hooks.crewai import install_crewai_hooks

registration = install_crewai_hooks(MemStrataClient.from_env())
try:
    result = crew.kickoff(inputs={"question": "What changed?"})
finally:
    registration.close()
```

The native `before_tool_call` hook gates every tool before its implementation
runs. The matching `after_tool_call` hook records completion with the same
trace ID. A `review` decision blocks by default. An application may supply a
`review_resolver` that returns an approval ID issued by MemStrata Governance;
the adapter then repeats the gate with that ID. For an attended terminal, the
opt-in `console_governance_review` helper asks the operator to paste that ID.

Before the first model call in each task, the adapter compiles bounded memory
from the original task text and inserts it as a clearly marked user-level
evidence message immediately before the real task. It is intentionally not a
system message because retrieved content may be untrusted and must not gain
instruction authority.
After the agent completes the task, a CrewAI event listener stores the task and
final answer as one conversational turn. Event replay is ignored so restoring
a CrewAI checkpoint does not write the same turn again.

CrewAI also offers a custom `StorageBackend`, but its search contract receives
an embedding rather than the original query. MemStrata's context compiler needs
the query text, temporal constraints, and project identity, so the adapter uses
the supported LLM hook instead of claiming false drop-in storage compatibility.

The implementation follows CrewAI's current official contracts:

* [Tool-call hooks](https://github.com/crewAIInc/crewAI/blob/main/lib/crewai/src/crewai/hooks/tool_hooks.py)
* [LLM-call hooks](https://github.com/crewAIInc/crewAI/blob/main/lib/crewai/src/crewai/hooks/llm_hooks.py)
* [Custom event listeners](https://docs.crewai.com/en/concepts/event-listener)
* [Unified memory and custom storage](https://docs.crewai.com/en/concepts/memory)

`memstrata_agent_hooks.adapters` retains the earlier dependency-free helpers
for compatibility, plus voluntary OpenAI-compatible tools and a generic
context manager usable by any Python agent.

Function tools are a convenience, not enforcement. A host must invoke `gate`
from its before-tool lifecycle hook to guarantee that denied work never runs.
The CLI covers hosts that only support executable stop hooks:

```bash
memstrata-hooks gate --agent hermes --tool terminal --input-json '{"command":"git status"}'
```

Exit code `0` means allow. Exit code `2` means the decision was `review` or
`block`. A daemon error uses exit code `1` for memory commands, while Governance
availability follows the chosen fail-open or fail-closed policy.

## Native host bundles

The wheel includes tested bundles for OpenClaw, Hermes, OpenCode, and a generic
NeoClaw bridge. List or copy them without downloading additional code:

```bash
memstrata-hooks list-hosts
memstrata-hooks copy-host openclaw ./memstrata-openclaw
memstrata-hooks copy-host hermes ./memstrata-hermes
memstrata-hooks copy-host opencode ./memstrata-opencode
memstrata-hooks copy-host neoclaw ./memstrata-neoclaw
```

Read the copied host README before enabling it. `copy-host` does not edit host
configuration or overwrite files unless `--overwrite` is supplied.

- OpenClaw uses the typed `before_tool_call`, `after_tool_call`,
  `before_prompt_build`, and `agent_end` plugin hooks.
- Hermes uses its supported model and tool lifecycle hooks in one plugin.
- OpenCode uses `tool.execute.before`, `tool.execute.after`, the current prompt
  transform, and session events.
- NeoClaw remains an explicit JSON executable bridge because several unrelated
  projects use that name and no stable common native plugin contract exists.

A Governance `review` never becomes permission merely because a host user clicks
yes. The call remains blocked until Governance supplies an approval and a new
gate returns `allow`. See [host plugin documentation](host_plugins/README.md)
and [integration status](INTEGRATION_STATUS.md).

## Operational boundary

The SDK is a transport layer. It does not duplicate the memory index, embed
documents, evaluate policy, certify regulatory compliance, or turn an unsigned
local cache into an audit record. Those responsibilities remain in the running
MemStrata products.
