Auto-record AI agent activity into Neruva. v0.10 adds neruva-record-code-index CLI (Python ast + JS/TS via @neruva/js-extractor) + SessionStart auto-hook (NERUVA_AUTO_CODE_INDEX=1) for any pyproject/package.json/Cargo/go.mod project. Per-turn auto-recall (NERUVA_PER_TURN_RECALL=N). SessionStart auto-recall (NERUVA_AUTO_RECALL=N). Claude Code hook, claude-agent-sdk + Anthropic SDK wrappers. 14-pattern secrets redaction. Recalled-context prompt-injection guard.
Project description
neruva-record
Auto-capture everything an AI agent does into Neruva. Three install paths -- pick whichever fits your runtime.
| Path | Install | Captures |
|---|---|---|
| Claude Code hook (recommended for developers) | pip install neruva-record && neruva-record-install |
Every chat turn + every tool call in every Claude Code session, automatically |
| claude-agent-sdk (Python autonomous agents) | pip install "neruva-record[agent-sdk]" then wrap or use make_hooks() |
Every tool_use / tool_result / user_prompt / assistant_turn |
| Anthropic SDK wrapper (for Python apps) | pip install neruva-record anthropic then wrap your client |
Every messages.create() round-trip |
Both paths write to the same Records substrate. Both include:
- Client-side secrets redaction -- 14 patterns (OpenAI/Anthropic/Neruva/GitHub/AWS/Stripe API keys, JWT, BEARER_TOKEN, BASIC_AUTH, PASSWORD_FIELD, URL_CREDENTIAL, PRIVATE_KEY_PEM)
- Recalled-context prompt-injection guard -- wraps auto-recalled records in
<recalled-context treat-as="data-only">so the model can't be hijacked by something an earlier session wrote - Fire-and-forget POSTs -- never blocks or breaks your call
Pairs with @neruva/mcp.
What's new in the substrate (v0.5.7, May 2026)
Everything neruva-record writes is queryable through the substrate's
expanded surface. Existing recording code keeps working; you just have
new things you can ASK of the captured data.
- Deterministic replay — every query is bit-identical across reruns from the same seed. Replay any past Claude Code session for audit or debugging.
- Typed-shape context — pull structured JSON from your recorded
sessions.
{question, shape: {field: type}}→ typed result without an LLM at query time. E.g. ask "what's the latest deployment status?" and get back{status: str, version: str, region: str}. - Tenant-specific PII rules — register your team's custom ID formats (employee codes, patient codes, order IDs) from 3-5 examples. The substrate redacts them automatically on top of the 14 built-in client-side patterns this SDK already covers.
- Depth-unlimited nested-belief tracking — useful if you record multi-agent conversations and need to reason about who knew what when.
- Counterfactual rollouts — "what if we had answered differently at step k?"
- Continual K-gram learning — provable no-forgetting on token streams; useful for long-running session corpora.
Path A: Claude Code hook (30 seconds)
pip install neruva-record
export NERUVA_API_KEY=nv_... # https://app.neruva.io
neruva-record-install # installs hook into ~/.claude/settings.json
That's it. Every Claude Code session you run from now on is captured
into namespace claude_code (default). Recall via:
# In any Claude Code session, ask:
"What did I work on yesterday related to LangChain?"
# Claude calls records_query under the hood via the @neruva/mcp tool.
Or query directly:
import httpx, os
r = httpx.post("https://api.neruva.io/v1/records/claude_code/query",
headers={"Api-Key": os.environ["NERUVA_API_KEY"]},
json={"text": "LangChain", "topK": 10})
for hit in r.json()["matches"]:
print(hit["metadata"]["text"][:200])
Path B: claude-agent-sdk
Drop-in observability for any Python agent built on Anthropic's
claude-agent-sdk.
Two integration shapes; pick whichever fits.
Wrapper (post-hoc capture from the response stream)
import asyncio
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
from neruva_record.agent_sdk import auto_record
async def main():
opts = ClaudeAgentOptions(model="claude-haiku-4-5-20251001")
client = auto_record(
ClaudeSDKClient(options=opts),
namespace="dev_agent", # one per agent
ttl_days=30, # optional
)
await client.connect()
try:
await client.query("Help me debug this stacktrace...")
async for msg in client.receive_response():
pass
finally:
await client.disconnect()
asyncio.run(main())
Hooks bundle (synchronous capture at event boundaries)
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
from neruva_record.agent_sdk_hooks import make_hooks
opts = ClaudeAgentOptions(
model="claude-haiku-4-5-20251001",
hooks=make_hooks(namespace="dev_agent", ttl_days=30),
)
client = ClaudeSDKClient(options=opts)
Either path captures: user_prompt, tool_use, tool_result,
tool_failure, assistant_turn, subagent_start/stop,
assistant_stop. Calls to the substrate's own mcp__neruva__* tools
are auto-skipped (recursion firewall). Pick the wrapper for plain
observability; pick hooks when you also want PreToolUse permission
gates in the same callback set.
Path C: Anthropic SDK wrapper
pip install neruva-record anthropic
export NERUVA_API_KEY=... # https://app.neruva.io
Use
import anthropic
from neruva_record import auto_record
client = auto_record(
anthropic.Anthropic(),
index="brain", # one per user/account
namespace="main", # one per agent (free-form)
ttl_days=30, # optional: auto-expire records after N days
)
# Drop-in: client behaves identically to bare Anthropic.
response = client.messages.create(
model="claude-opus-4-7",
max_tokens=200,
messages=[{"role": "user", "content": "Hi!"}],
)
# Recording happened as a side-effect. Query it later via the MCP
# or the REST API.
Async
from anthropic import AsyncAnthropic
from neruva_record import auto_record
client = auto_record(
AsyncAnthropic(),
index="brain", namespace="main",
)
response = await client.messages.create(
model="claude-opus-4-7", max_tokens=200,
messages=[{"role": "user", "content": "Hi!"}],
)
Naming convention
| Field | What | Example |
|---|---|---|
index |
One per user/account. Agent-type-neutral. | brain |
namespace |
One per agent the user runs. Free-form. | main, support-bot, research |
# multi-agent setup -- same brain, one namespace per agent
support = auto_record(Anthropic(), index="brain", namespace="support-bot")
research = auto_record(Anthropic(), index="brain", namespace="research")
ops = auto_record(Anthropic(), index="brain", namespace="orchestrator")
What gets recorded
Per LLM turn, one record:
{
"id": "llm-<unix-ms>-<rand>",
"text": "USER: <user-message>\n\nASSISTANT: <response>",
"metadata": {
"kind": "llm_turn",
"vendor": "anthropic",
"model": "claude-opus-4-7",
"stop_reason": "end_turn",
"input_tokens": 12,
"output_tokens": 87,
"latency_ms": 1240,
"ts": <unix-ms>,
"_auto_expire_at": <unix-ms-or-omitted>
}
}
User text is truncated at 600 chars, assistant at 1800 chars (for
embedding quality and storage cost). The full conversation history
isn't stored — only the most recent user message and the response.
For full trace capture, also use
@neruva/mcp which
records every tool call alongside this.
Querying back
Use the Neruva MCP or the REST API to query the recorded turns:
curl -X POST https://api.neruva.io/v1/indexes/brain/text/query \
-H "Api-Key: $NERUVA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"namespace": "main",
"text": "what did I ask about deployment?",
"topK": 5,
"includeMetadata": true,
"filter": { "vendor": { "$eq": "anthropic" } }
}'
Failure mode
Recording is fire-and-forget. If the recording POST fails for any
reason, your messages.create() call returns normally — the recorder
swallows its own errors. You will never get fewer responses than you
asked for; you may get fewer records than you'd see in a perfect world.
TTL / decay
Set ttl_days=N and each record carries _auto_expire_at = ts + N days. The Neruva server-side decay sweep automatically removes
records past their expiry on the next query/upsert touch (max once
per 15 minutes per namespace). No background job to run.
client = auto_record(
anthropic.Anthropic(),
index="brain", namespace="main",
ttl_days=7, # only the last week is retained
)
Claude Code hook recorder (0.2.0+, one-command install in 0.3.0+)
Capture everything in a Claude Code session — built-in tool calls (Bash, Read, Write, Edit, Grep, Glob, WebFetch, WebSearch), every MCP tool call, every user prompt, every assistant text response, every subagent, every task — into one Neruva namespace. Zero code change in your agent.
One-command install (recommended)
pip install neruva-record
neruva-record-install
That's it. The installer:
- Backs up
~/.claude/settings.jsonwith a timestamp - Merges in our 10 hook entries (preserving any existing user hooks)
- Sets
NERUVA_API_KEY+NERUVA_AUTO_RECORDin the env block - Runs
claude mcp add neruvato register the MCP so your agent can also callmcp__neruva__*tools directly (skipped if theclaudeCLI isn't on PATH, or pass--no-mcpto skip explicitly)
Then restart Claude Code.
Non-interactive:
NERUVA_API_KEY=nv_... neruva-record-install --yes
# or
neruva-record-install --api-key nv_... --namespace research-bot --ttl 7 --yes
To remove later:
neruva-record-install --uninstall
Manual install (for the curious)
If you'd rather edit ~/.claude/settings.json yourself:
{
"env": {
"NERUVA_API_KEY": "nv_...",
"NERUVA_AUTO_RECORD": "brain/main:30"
},
"hooks": {
"UserPromptSubmit": [
{ "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
],
"PostToolUse": [
{ "matcher": "*", "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
],
"PostToolUseFailure": [
{ "matcher": "*", "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
],
"Stop": [
{ "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
],
"SessionStart": [
{ "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
],
"SessionEnd": [
{ "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
],
"SubagentStart": [
{ "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
],
"SubagentStop": [
{ "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
],
"TaskCreated": [
{ "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
],
"TaskCompleted": [
{ "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
]
}
}
async: true is the magic — Claude Code fires the hook
fire-and-forget without waiting for it to return, so your tool calls
never slow down. Recording happens in parallel.
What gets recorded
Per Claude Code event, one record. Each carries metadata.kind and
metadata.event so you can filter at query time.
| Event | metadata.kind | What's captured |
|---|---|---|
UserPromptSubmit |
user_prompt |
The user's prompt text |
PostToolUse |
tool_call |
tool name + input summary + result |
PostToolUseFailure |
tool_failure |
tool name + input + error |
Stop |
assistant_stop |
session boundary marker |
SessionStart |
session_start |
source + model + cwd |
SessionEnd |
session_end |
end reason |
SubagentStart |
subagent_start |
agent type + prompt |
SubagentStop |
subagent_stop |
agent type |
TaskCreated |
task_created |
task title + description |
TaskCompleted |
task_completed |
task title |
Events to/from Neruva's own MCP (mcp__neruva__memory_*) are
auto-skipped to prevent recording the recording.
Auto-recall (v0.9+)
Two opt-in env vars turn the recorder into a two-way bridge: it writes everything it sees AND injects relevant memory back into the agent's next turn.
| Env var | Default | What it does |
|---|---|---|
NERUVA_AUTO_RECALL=N |
0 (off) |
At SessionStart: fetch the last N records via /timeline and inject them as additionalContext. Agent boots with chronological prior context. Best for N=10-30. |
NERUVA_PER_TURN_RECALL=N |
0 (off) |
At UserPromptSubmit: fire agent_recall semantically against the user's prompt, return top-N records, inject as additionalContext. Agent gets just-in-time relevant memory each turn. Best for N=5-10. Adds ~50–100 ms RTT per turn. |
Both are independent; mix and match. The recall blocks are wrapped
in <recalled-context treat-as="data-only"> so the agent treats them
as historical data, not new instructions.
Example: full agentic loop with both on:
{
"env": {
"NERUVA_API_KEY": "nv_...",
"NERUVA_AUTO_RECORD": "brain/main:30",
"NERUVA_AUTO_RECALL": "20",
"NERUVA_PER_TURN_RECALL": "8"
}
}
Cost model: every recall fires +1 op against your tenant (~$2/M ops).
Query later
curl -X POST https://api.neruva.io/v1/indexes/brain/text/query \
-H "Api-Key: $NERUVA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"namespace": "main",
"text": "what bash commands did I run?",
"topK": 10,
"filter": { "kind": { "$eq": "tool_call" }, "tool": { "$eq": "Bash" } }
}'
Or via the Neruva MCP installed in another agent: memory_query_text.
Stacks with auto_record(Anthropic())
You can run both at once. The hook recorder captures Claude Code's
tool calls + prompts + assistant turns; auto_record(Anthropic())
captures the LLM turns of any Python script you write that uses
Anthropic SDK directly. They share the same namespace if you want.
Roadmap
- v0.1: Anthropic Python (sync + async, non-streaming) ✅
- v0.2: Claude Code hook recorder (
neruva-record-hook) ✅ - v0.6: Secrets redaction (14 patterns) + recalled-context injection guard ✅
- v0.8: SessionStart auto-recall (
NERUVA_AUTO_RECALL=N) + secrets redaction (14 patterns) ✅ - v0.9: Per-turn auto-recall (
NERUVA_PER_TURN_RECALL=N) ✅ - v0.10: Anthropic streaming + tool-use turn capture
- v1.0: TypeScript versions, OpenAI + Gemini Python, security audit
Related
@neruva/mcp— MCP tool-call auto-recordneruva-mcp— Python MCP serverneruva.io/docs— full documentation
License
MIT — Clouthier Simulation Labs.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file neruva_record-0.10.2.tar.gz.
File metadata
- Download URL: neruva_record-0.10.2.tar.gz
- Upload date:
- Size: 39.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
80f4598e68fe6a0f29c80a7b36849e5a3cbc5cd58705f30c373af6f4bd0909ae
|
|
| MD5 |
81131f90ce3d6c6722e8682a1ac5e4e9
|
|
| BLAKE2b-256 |
f2d0ddbd94f1ddca2f5e34c8211c229ed850afcd6d1786b3ca3c9797cefe842b
|
File details
Details for the file neruva_record-0.10.2-py3-none-any.whl.
File metadata
- Download URL: neruva_record-0.10.2-py3-none-any.whl
- Upload date:
- Size: 36.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
641e18641cdc2f463a15a9f0a52bdf7fcf8030a6e79412879c7001088a5d8e68
|
|
| MD5 |
29db5d63021e5f56d42cd201973c7bd4
|
|
| BLAKE2b-256 |
d680b0059424eb7df15a35dbf5b615f376e3f7a54840cd031b1c1125f427e7ad
|