Files
Nicolò Boschi d7d137fa1e fix(daemon): drop the idle timeout that could kill an in-flight request (#3930)
* fix(daemon): drop the idle timeout that could kill an in-flight request

The daemon's idle checker measured idleness as "time since the last request
*started*" (IdleTimeoutMiddleware stamped last_activity on entry, never on
completion), so a retain, reflect or consolidation that outlived the configured
timeout was SIGTERM'd mid-flight — the client saw a connection error and the
work was lost. Raising the timeout only lowered the odds; 0 (the default
everywhere but the legacy cursor hook package) was the only safe value.

Rather than teach the middleware to count in-flight requests, remove the
feature: a shared local daemon that quietly exits under a long operation is not
worth the resource reclamation it buys. The daemon now runs until it is
stopped.

- Delete IdleTimeoutMiddleware, the idle-checker thread and DEFAULT_IDLE_TIMEOUT.
- Keep `--idle-timeout` parseable — hindsight-embed and every coding-agent
  integration still pass it — but ignore it, printing a note when it is
  non-zero. The integration packages therefore need no change and no release.
- Drop it from the current docs, the SDK/integration READMEs and the generated
  docs skill. The coding-agents README keeps the row marked deprecated because
  docs-freshness.test.ts requires every readable RawConfig field to be
  documented; versioned docs, blog posts and changelogs are left as history.

Closes #3903

Claude-Session: https://claude.ai/code/session_012gXUp1i7YrWmLJVUYki53g

* review: drop the now-dead logger and refresh the comments the removal invalidated

- daemon.py: the module logger only served the deleted idle checker.
- coding-agents daemon.ts / runtime.ts / runtime.test.ts and openclaw: comments,
  a debug line and the plugin-schema description still described an auto-exit
  that can no longer happen. The config fields stay (inert) as agreed.

Claude-Session: https://claude.ai/code/session_012gXUp1i7YrWmLJVUYki53g
2026-08-31 15:59:31 +02:00

324 lines
21 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
sidebar_position: 5
title: "Claude Code Persistent Memory with Hindsight | Integration"
description: "Add long-term memory to Claude Code with Hindsight. Automatically captures conversations and recalls relevant context across sessions using Claude Code's hook-based architecture."
---
# Claude Code
:::warning Superseded by the Coding Agents plugin
**The Claude Code plugin** is superseded by the [Coding Agents plugin](/sdks/integrations/coding-agents) — one
package covering Claude Code, Codex, opencode, Kilo, Cursor, Copilot, Grok, Antigravity, Devin and Cline and other CLI agents, with a per-repo memory bank they all share instead
of one bank per agent.
This page and the published package still work; they are no longer developed. To switch:
```bash
cd /path/to/your/repo
npx @vectorize-io/hindsight-coding-agents install claude-code --import-conversations
```
Two things move, and nothing else does.
**Your server moves automatically.** `install` reads `~/.hindsight/claude-code.json` and keeps the same
`apiUrl` and token — an empty URL still means the local daemon — so you are not silently switched
to Hindsight Cloud. Pass `--server` to change it.
**Your conversations are re-imported from local transcripts** by `--import-conversations`, as new
documents. They are not copied from the old bank: that bank's default was a single static bank
shared by every project, and its documents record only a session id, so working out which ones
belong to this repo means reading the local transcripts anyway.
**Nothing else carries over.** The old `recall*` and `retain*` settings, missions and bank-naming
options describe a pipeline this package replaced. Bank naming changes too — one bank per repo,
shared by every agent. See
[Migrating from the per-agent plugins](/sdks/integrations/coding-agents#migrating-from-the-per-agent-plugins).
:::
Biomimetic long-term memory for [Claude Code](https://docs.anthropic.com/en/docs/claude-code) using [Hindsight](https://vectorize.io/hindsight). Automatically captures conversations, recalls relevant context, and exposes knowledge tools and a subagent-creation skill so Claude can read and write its own memory.
[View Changelog →](/changelog/integrations/claude-code)
:::tip Works in Grok Build too
Grok Build natively supports Claude Code plugins. See the [Grok Build integration guide](/sdks/integrations/grok-build) for Grok-specific setup.
:::
## Quick Start
```bash
# 1. Add the Hindsight marketplace and install the plugin
claude plugin marketplace add vectorize-io/hindsight
claude plugin install hindsight-memory
# 2. Configure your LLM provider for memory extraction
# Option A: OpenAI (auto-detected)
export OPENAI_API_KEY="sk-your-key"
# Option B: Anthropic (auto-detected)
export ANTHROPIC_API_KEY="your-key"
# Option C: No API key needed (uses Claude Code's own model — personal/local use only)
export HINDSIGHT_LLM_PROVIDER=claude-code
# Option D: Connect to an external Hindsight server instead of running locally
mkdir -p ~/.hindsight
echo '{"hindsightApiUrl": "https://your-hindsight-server.com"}' > ~/.hindsight/claude-code.json
# 3. Start Claude Code — the plugin activates automatically
claude
```
That's it! The plugin will automatically start capturing and recalling memories.
## Features
- **Auto-recall** — on every user prompt, queries Hindsight for relevant memories and injects them as context (invisible to the chat transcript, visible to Claude)
- **Auto-retain** — after every response (or every N turns), extracts and retains conversation content to Hindsight for long-term storage
- **Knowledge tools** — Claude can read, write, and search its own memory via MCP tools (knowledge pages, recall, ingest)
- **Subagent skill** — `/hindsight-memory:create-agent` scaffolds a subagent backed by an isolated memory bank
- **Dynamic bank IDs** — per-agent, per-project, or per-session memory isolation (so each subagent gets its own brain)
- **Daemon management** — can auto-start/stop `hindsight-embed` locally or connect to an external Hindsight server
- **Channel-agnostic** — works with Claude Code Channels (Telegram, Discord, Slack) or interactive sessions
## Architecture
The plugin combines hooks (for automatic recall/retain) with an MCP server (for explicit knowledge tools) and a skill (for subagent creation):
| Component | Trigger | Purpose |
|-----------|---------|---------|
| `session_start.py` | `SessionStart` hook | Health check — verify Hindsight is reachable |
| `recall.py` | `UserPromptSubmit` hook | **Auto-recall** — query memories, inject as `additionalContext` |
| `retain.py` | `Stop` hook | **Auto-retain** — extract transcript, POST to Hindsight (async) |
| `session_end.py` | `SessionEnd` hook | Cleanup — stop auto-managed daemon if started |
| `mcp_server.py` | MCP server | Exposes `agent_knowledge_*` tools — list/get/create/update/delete pages, recall, ingest |
| `create-agent` | Skill | Scaffolds a subagent file under `~/.claude/agents/` and seeds its bank |
Python dependencies (`mcp`) are bootstrapped on first run into a private venv under `${CLAUDE_PLUGIN_DATA}/venv` — no global pip install, isolated to the plugin, survives plugin updates.
## Connection Modes
### 1. External API (recommended for production)
Connect to a running Hindsight server (cloud or self-hosted). No local LLM needed — the server handles fact extraction.
```json
{
"hindsightApiUrl": "https://your-hindsight-server.com",
"hindsightApiToken": "your-token"
}
```
### 2. Local Daemon (auto-managed)
The plugin automatically starts and stops `hindsight-embed` via `uvx`. Requires an LLM provider API key for local fact extraction.
Set an LLM provider:
```bash
export OPENAI_API_KEY="sk-your-key"
# or
export ANTHROPIC_API_KEY="your-key"
# or
export HINDSIGHT_LLM_PROVIDER=claude-code # No API key needed
```
The model is selected automatically by the Hindsight API. To override, set `HINDSIGHT_LLM_MODEL`.
### 3. Existing Local Server
If you already have `hindsight-embed` running, leave `hindsightApiUrl` empty and set `apiPort` to match your server's port. The plugin will detect it automatically.
## Configuration
All settings live in `~/.hindsight/claude-code.json`. Every setting can also be overridden via environment variables. The plugin ships with sensible defaults — you only need to configure what you want to change.
**Loading order** (later entries win):
1. Built-in defaults (hardcoded in the plugin)
2. Plugin `settings.json` (ships with the plugin, at `CLAUDE_PLUGIN_ROOT/settings.json`)
3. User config (`~/.hindsight/claude-code.json` — recommended for your overrides)
4. Environment variables
---
### Connection & Daemon
These settings control how the plugin connects to the Hindsight API.
| Setting | Env Var | Default | Description |
|---------|---------|---------|-------------|
| `hindsightApiUrl` | `HINDSIGHT_API_URL` | `""` (empty) | URL of an external Hindsight API server. When empty, the plugin uses a local daemon instead. |
| `hindsightApiToken` | `HINDSIGHT_API_TOKEN` | `null` | Authentication token for the external API. Only needed when `hindsightApiUrl` is set. |
| `apiPort` | `HINDSIGHT_API_PORT` | `9077` | Port used by the local `hindsight-embed` daemon. Change this if you run multiple instances or have a port conflict. |
| `embedVersion` | `HINDSIGHT_EMBED_VERSION` | `"latest"` | Which version of `hindsight-embed` to install via `uvx`. Pin to a specific version (e.g. `"0.5.2"`) for reproducibility. |
| `embedPackagePath` | `HINDSIGHT_EMBED_PACKAGE_PATH` | `null` | Local filesystem path to a `hindsight-embed` checkout. When set, the plugin runs from this path instead of installing via `uvx`. Useful for development. |
| `requestTimeoutSeconds` | `HINDSIGHT_REQUEST_TIMEOUT_SECONDS` | `null` | Overrides the per-call request timeout for recall (default `10s`), retain (default `15s`) and knowledge tool calls (`10–15s`). When unset, the per-call defaults are preserved. Bump this when self-hosted Hindsight legitimately takes longer than 10s under contention (e.g. parallel recalls), to avoid client-side `read operation timed out` errors on requests the server completes successfully. Does not affect the health check, which intentionally stays fast (5s). |
---
### LLM Provider (local daemon only)
These settings configure which LLM the local daemon uses for fact extraction. They are **ignored** when connecting to an external API (the server uses its own LLM configuration).
| Setting | Env Var | Default | Description |
|---------|---------|---------|-------------|
| `llmProvider` | `HINDSIGHT_LLM_PROVIDER` | auto-detect | Which LLM provider to use. Supported values: `openai`, `anthropic`, `gemini`, `groq`, `ollama`, `ollama-cloud`, `openai-codex`, `claude-code`. When omitted, the plugin auto-detects by checking for API key env vars in order: `OPENAI_API_KEY` → `ANTHROPIC_API_KEY` → `GEMINI_API_KEY` → `GROQ_API_KEY`. |
| `llmModel` | `HINDSIGHT_LLM_MODEL` | provider default | Override the default model for the chosen provider (e.g. `"gpt-4o"`, `"claude-sonnet-4-20250514"`). When omitted, the Hindsight API picks a sensible default for each provider. |
| `llmApiKeyEnv` | — | provider standard | Name of the environment variable that holds the API key. Normally auto-detected (e.g. `OPENAI_API_KEY` for the `openai` provider). Set this only if your key is in a non-standard env var. |
---
### Memory Bank
A **bank** is an isolated memory store — like a separate "brain." These settings control which bank the plugin reads from and writes to.
| Setting | Env Var | Default | Description |
|---------|---------|---------|-------------|
| `bankId` | `HINDSIGHT_BANK_ID` | `"claude_code"` | The bank ID to use when `dynamicBankId` is `false`. All sessions share this single bank. |
| `bankMission` | `HINDSIGHT_BANK_MISSION` | generic assistant prompt | A short description of the agent's identity and purpose. Sent to Hindsight when creating or updating the bank, and used during recall to contextualize results. |
| `retainMission` | — | extraction prompt | Instructions for the fact extraction LLM — tells it *what* to extract from conversations (e.g. "Extract technical decisions and user preferences"). |
| `dynamicBankId` | `HINDSIGHT_DYNAMIC_BANK_ID` | `false` | When `true`, the plugin derives a unique bank ID from context fields (see `dynamicBankGranularity`), giving each combination its own isolated memory. |
| `dynamicBankGranularity` | — | `["agent", "project"]` | Which context fields to combine when building a dynamic bank ID. Available fields: `agent` (agent name), `project` (working directory), `session` (session ID), `channel` (channel ID), `user` (user ID). |
| `bankIdPrefix` | — | `""` | A string prepended to all bank IDs — both static and dynamic. Useful for namespacing (e.g. `"prod"` or `"staging"`). |
| `agentName` | `HINDSIGHT_AGENT_NAME` | `"claude-code"` | Name used for the `agent` field in dynamic bank ID derivation. |
| `resolveWorktrees` | — | `true` | When deriving the `project` field, resolve git worktrees to the **main repository's basename** so that all worktrees of the same repo share one bank. Set to `false` to use the literal working directory basename instead (each worktree gets its own bank). |
| `directoryBankMap` | — | `{}` | Explicit `{ "/path/to/dir": "bank-id" }` mapping. When the current working directory matches an entry, that bank is used directly — overrides both static and dynamic resolution. `bankIdPrefix` still applies on top. |
#### Worktrees and explicit mapping
By default, all git worktrees of the same repository share one bank. For example, if you keep a main checkout at `/home/me/myproject` and a worktree at `/home/me/myproject-wt1`, both resolve to `project = "myproject"` — so dynamic bank IDs like `claude-code::myproject` are stable across all worktrees. This avoids fragmenting memory across short-lived branches.
If you want each worktree to have its own bank, set `"resolveWorktrees": false`.
For full control, use `directoryBankMap` to pin specific directories to specific bank IDs:
```json
{
"directoryBankMap": {
"/home/me/work/client-a": "client-a-memories",
"/home/me/personal/blog": "blog"
}
}
```
When `cwd` matches one of the keys, that bank is used immediately — no static or dynamic resolution runs. Directories not listed fall through to the normal logic.
---
### Auto-Recall
Auto-recall runs on every user prompt. It queries Hindsight for relevant memories and injects them into Claude's context as invisible `additionalContext` (the user doesn't see them in the chat transcript).
| Setting | Env Var | Default | Description |
|---------|---------|---------|-------------|
| `autoRecall` | `HINDSIGHT_AUTO_RECALL` | `true` | Master switch for auto-recall. Set to `false` to disable memory retrieval entirely. |
| `recallBudget` | `HINDSIGHT_RECALL_BUDGET` | `"mid"` | Controls how hard Hindsight searches for memories. `"low"` = fast, fewer strategies; `"mid"` = balanced; `"high"` = thorough, slower. Affects latency directly. |
| `recallMaxTokens` | `HINDSIGHT_RECALL_MAX_TOKENS` | `1024` | Maximum number of tokens in the recalled memory block. Lower values reduce context usage but may truncate relevant memories. |
| `recallTypes` | — | `["observation"]` | Which memory types to retrieve. `"world"` = general facts; `"experience"` = personal experiences; `"observation"` = consolidated, deduplicated beliefs built from multiple facts. Defaults to observations so the same answer doesn't surface multiple times when many raw memories say the same thing. |
| `recallContextTurns` | `HINDSIGHT_RECALL_CONTEXT_TURNS` | `1` | How many prior conversation turns to include when composing the recall query. `1` = only the latest user message; higher values give more context but may dilute the query. |
| `recallMaxQueryChars` | `HINDSIGHT_RECALL_MAX_QUERY_CHARS` | `800` | Maximum character length of the query sent to Hindsight. Longer queries are truncated. |
| `recallRoles` | — | `["user", "assistant"]` | Which message roles to include when building the recall query from prior turns. |
| `recallPromptPreamble` | — | built-in string | Text placed above the recalled memories in the injected context block. Customize this to change how Claude interprets the memories. |
---
### Auto-Retain
Auto-retain runs after Claude responds. It extracts the conversation transcript and sends it to Hindsight for long-term storage and fact extraction.
| Setting | Env Var | Default | Description |
|---------|---------|---------|-------------|
| `autoRetain` | `HINDSIGHT_AUTO_RETAIN` | `true` | Master switch for auto-retain. Set to `false` to disable memory storage entirely. |
| `retainMode` | `HINDSIGHT_RETAIN_MODE` | `"full-session"` | Retention strategy. `"full-session"` sends the full conversation transcript (with chunking). |
| `retainEveryNTurns` | — | `10` | How often to retain. `1` = every turn; `10` = every 10th turn. Higher values reduce API calls but delay memory capture. Values > 1 enable **chunked retention** with a sliding window. |
| `retainOverlapTurns` | — | `2` | When chunked retention fires, this many extra turns from the previous chunk are included for continuity. Total window size = `retainEveryNTurns + retainOverlapTurns`. |
| `retainRoles` | — | `["user", "assistant"]` | Which message roles to include in the retained transcript. |
| `retainToolCalls` | — | `true` | Whether to include tool calls (function invocations and results) in the retained transcript. Captures structured actions like file reads, searches, and code edits. |
| `retainTags` | — | `["{session_id}"]` | Tags attached to the retained document. Supports `{session_id}` placeholder which is replaced with the current session ID at runtime. |
| `retainMetadata` | — | `{}` | Arbitrary key-value metadata attached to the retained document. |
| `retainContext` | — | `"claude-code"` | A label attached to retained memories identifying their source. Useful when multiple integrations write to the same bank. |
---
### Knowledge Tools
The plugin runs an MCP server that exposes `agent_knowledge_*` tools, letting Claude explicitly read, write, and search its memory bank — as opposed to the hook-driven recall/retain which is fully automatic. None of the tools accept a `bank_id` parameter; the server resolves the active bank from the same config as the hooks, so tools and hooks always agree on which bank they touch.
| Tool | Purpose |
|------|---------|
| `agent_knowledge_get_current_bank` | Reports the bank ID the current session is bound to. |
| `agent_knowledge_list_pages` | Lists all knowledge pages (mental models) with IDs and names. |
| `agent_knowledge_get_page` | Reads the full synthesized content of a specific page. |
| `agent_knowledge_create_page` | Creates a new knowledge page with a `source_query` that re-synthesizes content after each consolidation. |
| `agent_knowledge_update_page` | Updates a page's name or `source_query`. |
| `agent_knowledge_delete_page` | Permanently deletes a knowledge page. |
| `agent_knowledge_recall` | Searches across retained conversations and documents for specific facts. |
| `agent_knowledge_ingest` | Uploads raw text content into the bank as a document. |
| `agent_knowledge_ingest_file` | Reads a file from disk and ingests its full content. |
| Setting | Env Var | Default | Description |
|---------|---------|---------|-------------|
| `enableKnowledgeTools` | `HINDSIGHT_ENABLE_KNOWLEDGE_TOOLS` | `true` | Master switch for the MCP knowledge tools. When `false`, the MCP server stays alive as an empty server exposing no `agent_knowledge_*` tools (it does not exit — exiting would trigger a `-32000` reconnect error in Claude Code). |
---
### Subagents with Memory
The `/hindsight-memory:create-agent` skill creates a Claude Code subagent backed by its own Hindsight bank. Each subagent's prompt instructs it to use the knowledge tools above for recall and ingestion, so Claude treats persistent memory as a first-class capability of that agent.
Two modes:
- **From a directory** — `/hindsight-memory:create-agent <name> from <path>` ingests every text file under `<path>` (`.md`, `.txt`, `.html`, `.json`, `.csv`, `.xml`), creates a starter set of knowledge pages, and writes the subagent file to `~/.claude/agents/<name>.md`. If the directory contains a `bank-template.json` listing exact `mental_models`, those are used verbatim instead of auto-generated.
- **Interactive** — `/hindsight-memory:create-agent` (no arguments) prompts for the agent name and purpose, then writes the subagent file with no ingestion.
For per-subagent memory isolation, enable dynamic bank IDs:
```json
{
"dynamicBankId": true,
"dynamicBankGranularity": ["agent", "project"],
"enableKnowledgeTools": true
}
```
With `["agent", "project"]`, each subagent gets a unique bank per repository — `<agent-name>::<project-basename>`. Switch to `["agent"]` for a single bank per subagent across all projects, or `["agent", "session"]` for ephemeral per-session banks.
---
### Debug
| Setting | Env Var | Default | Description |
|---------|---------|---------|-------------|
| `debug` | `HINDSIGHT_DEBUG` | `false` | Enable verbose logging to stderr. All log lines are prefixed with `[Hindsight]`. Useful for diagnosing connection issues, recall/retain behavior, and bank ID derivation. |
## Claude Code Channels
With [Claude Code Channels](https://docs.anthropic.com/en/docs/claude-code), Claude Code can operate as a persistent background agent connected to Telegram, Discord, Slack, and other messaging platforms. This plugin gives Channel-based agents the same long-term memory that `hindsight-openclaw` provides for Openclaw agents.
For Channel agents, enable dynamic bank IDs for per-channel/per-user memory isolation:
```json
{
"dynamicBankId": true,
"dynamicBankGranularity": ["agent", "channel", "user"]
}
```
And set channel context via environment variables:
```bash
export HINDSIGHT_CHANNEL_ID="telegram-group-12345"
export HINDSIGHT_USER_ID="user-67890"
```
## Troubleshooting
**Plugin not activating**: Check Claude Code logs for `[Hindsight]` messages. Enable `"debug": true` in `~/.hindsight/claude-code.json`.
**Recall returning no memories**: Verify the Hindsight server is reachable (`curl http://localhost:9077/health`). Memories need at least one retain cycle before they're available.
**Daemon not starting**: Ensure an LLM API key is set (or use `HINDSIGHT_LLM_PROVIDER=claude-code`). Review daemon logs at `~/.hindsight/profiles/claude-code.log`.
**High latency on recall**: The recall hook has a 12-second timeout. Use `recallBudget: "low"` or reduce `recallMaxTokens` for faster responses.
**Knowledge tools missing from Claude**: Make sure `enableKnowledgeTools` is `true` in `~/.hindsight/claude-code.json` and restart Claude Code. The first launch after enabling them takes ~20–30 seconds to bootstrap the Python venv at `${CLAUDE_PLUGIN_DATA}/venv`; subsequent launches are instant.