Metadata-Version: 2.4
Name: neruva-record
Version: 0.3.0
Summary: Drop-in auto-record wrapper for the Anthropic Python SDK — every LLM turn captured to Neruva Memory. Recall later via the Neruva MCP / API. Cross-session, cross-vendor, portable.
Author-email: Clouthier Simulation Labs <info@neruva.io>
License: MIT
Project-URL: Homepage, https://neruva.io/
Project-URL: Documentation, https://neruva.io/docs/
Project-URL: Source, https://github.com/CloutSimLabs/neruva
Keywords: neruva,anthropic,claude,agent-memory,auto-record,observability,llm
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.27
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.30; extra == "anthropic"

# neruva-record

Drop-in auto-record wrapper for the Anthropic Python SDK. Every
`messages.create()` call captures both directions to a Neruva Memory
namespace as a side-effect. Stop manually saving things; query the
namespace later for "what did this agent do?"

Pairs with [`@neruva/mcp`](https://www.npmjs.com/package/@neruva/mcp)
(captures MCP tool calls). Together: full agent-loop capture.

## Install

```bash
pip install neruva-record anthropic
export NERUVA_API_KEY=...        # https://app.neruva.io
```

## Use

```python
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

```python
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` |

```python
# 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:

```json
{
  "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`](https://www.npmjs.com/package/@neruva/mcp) which
records every tool call alongside this.

## Querying back

Use the [Neruva MCP](https://www.npmjs.com/package/@neruva/mcp) or the
REST API to query the recorded turns:

```bash
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.

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

```bash
pip install neruva-record
neruva-record-install
```

That's it. The installer reads your `~/.claude/settings.json`,
backs it up with a timestamp, merges in our hook entries (preserving
any existing hooks you have), and prompts for your API key + which
namespace to use. Restart Claude Code after.

Non-interactive:

```bash
NERUVA_API_KEY=nv_... neruva-record-install --yes
# or
neruva-record-install --api-key nv_... --namespace research-bot --ttl 7 --yes
```

To remove later:

```bash
neruva-record-install --uninstall
```

### Manual install (for the curious)

If you'd rather edit `~/.claude/settings.json` yourself:

```json
{
  "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.

### Query later

```bash
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.3: Anthropic streaming + tool-use turn capture
- v0.4: OpenAI Python
- v0.5: Google Gemini Python
- v0.6: TypeScript versions of all the above
- v1.0: Stable API, security audit

## Related

- [`@neruva/mcp`](https://www.npmjs.com/package/@neruva/mcp) — MCP tool-call auto-record
- [`neruva-mcp`](https://pypi.org/project/neruva-mcp/) — Python MCP server
- [`neruva.io/docs`](https://neruva.io/docs/) — full documentation

## License

MIT — Clouthier Simulation Labs.
