* feat(api): add bank template import/export endpoints
Add POST /banks/{bank_id}/import and GET /banks/{bank_id}/export
endpoints for declarative bank setup via JSON manifests.
A template manifest (version 1) can include bank config overrides
and mental model definitions. Import creates or updates mental
models matched by id, applies config as per-bank overrides, and
returns async operation IDs for content generation.
Export dumps a bank's explicit overrides and mental models as a
manifest that can be re-imported into another bank.
Includes control plane UI: bank creation dialog now accepts an
optional template JSON to pre-configure the bank on creation.
* docs: add Template Gallery page and bank templates reference
- Template Gallery (/templates) with search, category filter, manifest
preview modal with copy-to-clipboard
- 5 starter templates: Customer Support, Research Assistant, Personal
Journal, Code Review Buddy, Meeting Notes
- Bank Templates API reference doc (developer/api/bank-templates)
- Sidebar entry under API section
* docs: add Template Gallery links to navbar and sidebar
- Top navbar: "Templates" link between Integrations and Changelog
- Sidebar: "Template Gallery" in Resources section
* fix(docs): remove emoji icons, autofocus search, fix placeholder in template gallery
* docs: rename to Bank Templates, move to Resources sidebar only
* docs: add Bank Templates to Resources navbar dropdown
* feat(api): add directives to bank template import/export
- Add BankTemplateDirective model with name, content, priority, is_active, tags
- Import creates/updates directives matched by name
- Export includes all directives (active and inactive)
- Validation: duplicate names rejected, empty name/content caught
- Tests: 24 tests covering directives create/update, existing vs new
bank import, validation, export with directives, full round-trip
* docs: add directives to bank templates docs and sample templates
* feat(api): add JSON Schema endpoint for bank template validation
- GET /v1/default/bank-template-schema returns the JSON Schema
auto-generated from the Pydantic BankTemplateManifest model
- Static schema file at docs/static/bank-template-schema.json
- Docs updated with schema endpoint, static file link, and
validation examples (Python jsonschema, Node ajv-cli)
* feat(api): live schema validation on import, fix schema endpoint path
- Move schema endpoint to /v1/bank-template-schema (system-level, not per-bank)
- Import endpoint now accepts raw JSON and validates with Pydantic manually,
returning clean 400 errors instead of raw 422s for all validation failures
- All validation (schema + semantic) returns consistent 400 with detailed messages
* docs: add interactive JSON Schema viewer to Bank Templates page
Renders the Pydantic-generated schema as a collapsible property tree
with types, required badges, defaults, and descriptions. The schema
is imported from the static bank-template-schema.json file.
* ui: add template toggle switch and browse link to bank creation dialog
- Replace always-visible textarea with a switch toggle ("Import from template")
- Textarea only shows when switch is on, keeping the dialog clean by default
- Add "Browse templates" link pointing to hindsight.vectorize.io/templates
- Reset template state when switch is toggled off or dialog is cancelled
* ui: add empty state with Add Document CTA to data view
When a bank has 0 memories, the data view (all tabs: constellation,
graph, table, timeline) shows a centered empty state with a CTA
button that opens the Add Document dialog.
* docs: replace templates with Conversation and Coding Agent
Remove generic placeholder templates. Add two practical templates
based on actual integration patterns:
- Conversation: for chat agents (LiteLLM, LangGraph, Pydantic AI,
Vercel AI SDK). Tracks user preferences, open threads.
- Coding Agent: for Claude Code/Codex. Tracks technical decisions,
project context, developer preferences. High literalism.
* docs: rename gallery to Bank Templates Hub, keep API doc as Bank Templates
* docs: register layout-template and file-json icons in navbar and sidebar
* docs: register layout-template icon in DefaultNavbarItem for dropdown items
* docs: show integration icons on template cards
Templates now have an optional `integrations` field referencing
integration IDs from integrations.json. Icons are resolved at render
time and shown in the card header next to the category badge.
* docs: add Personal Assistant template for OpenClaw, Hermes, NemoClaw
* feat: add Export Template to bank actions + map all integrations to templates
- Add "Export Template" to the bank Actions dropdown — exports config,
mental models, and directives as JSON, copies to clipboard
- Add export API route and client method
- Map remaining integrations to templates: CrewAI, AG2, Agno, Strands,
LlamaIndex, local-mcp, skills → Conversation; hindclaw → Personal Assistant
* feat: add --template flag to LoCoMo benchmark + remove schema from Hub
- LoCoMo benchmark accepts --template <path> to apply a bank template
manifest (config, mental models, directives) before ingestion
- Template is applied per-bank in both single-phase and two-phase modes
- BenchmarkRunner.apply_template() reuses the same engine methods as
the /import API endpoint
- Remove Manifest Schema section from Bank Templates Hub page
(schema stays in the API reference doc)
* refactor: remove description field from bank template manifest
* docs: remove tags, fact_types, and directives from starter templates
* docs: remove reflect_mission and disposition fields from starter templates
* build: validate template manifests against JSON Schema during docs build
* cleanup: remove unused JsonSchemaViewer component
* docs: remove retain_extraction_mode from starter templates
* ui: enable word wrap in template manifest preview
* docs: add link to Bank Templates reference doc from Hub page
* docs: convert bank templates doc to mdx with multi-language code snippets
- Convert bank-templates.md to .mdx with Tabs/CodeSnippet components
- Add example files: bank-templates.py, .mjs, .sh, .go with doc markers
- Examples cover import, dry-run, export, round-trip, and schema
- Regenerate OpenAPI spec and all client SDKs (Python, TS, Rust, Go)
* fix: migration revision collision + use typed models in benchmark template
- Rename merge migration d6e7f8a9b0c1 -> d6e7f8a9b0c2 to resolve
revision ID collision with case_insensitive_entities_trgm_index
- Update a4b5c6d7e8f9 down_revision to point to the renamed migration
- Fix f-string lint in case_insensitive migration
- BenchmarkRunner.apply_template() now validates manifest through
BankTemplateManifest Pydantic model instead of raw dict access
- Remove redundant inline imports (json, Path already at module top)
* fix(docs): add missing Go tab to dry-run code snippet
* ci: retrigger
* fix: sync skills openapi.json + fix bankId null type error in export
- Copy updated openapi.json to skills/hindsight-docs/references/
- Add null guard for bankId in Export Template onClick handler
* fix: sync generated files (memory_engine formatting, docs skill references)
* cleanup: remove obsolete migration collision workaround
* feat: add detail parameter to list/get mental models (#825)
Add a `detail` query parameter (metadata|content|full) to both list and get
mental model endpoints (HTTP + MCP) to control response size. This reduces
payload for agent boot flows and MCP clients where context budget is limited.
Closes#825
* fix: update Rust CLI for optional mental model fields
The generated Rust client now has content/source_query as Option<String>
after the detail parameter was added. Update CLI code to handle optionals.
* feat: add AutoGen integration for Hindsight
Adds hindsight-autogen package providing FunctionTool instances that give
AutoGen agents persistent long-term memory via retain/recall/reflect APIs.
- Package: hindsight_autogen with create_hindsight_tools() factory
- 31 unit tests covering tool creation, invocation, config fallback, errors
- Docs page and integrations.json entry
- README with quickstart and configuration reference
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address PR review feedback for autogen integration
- Fix install instructions to include autogen-agentchat and autogen-ext[openai]
- Add autogen.svg icon to prevent broken image in integrations grid
- Change icon reference from .png to .svg in integrations.json
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add sleep between retain/recall and close clients in examples
- Add time.sleep(3) between retain and recall to wait for async processing
- Close Hindsight client and model client to avoid unclosed session warnings
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use asyncio.sleep instead of time.sleep in async examples
time.sleep blocks the event loop; asyncio.sleep yields control.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address PR review feedback - validation, defaults, release script
- Add autogen to VALID_INTEGRATIONS in release-integration.sh
- Remove unused verbose config field
- Extract DEFAULT_BUDGET/MAX_TOKENS/RECALL_TAGS_MATCH constants in config.py,
import from tools.py to eliminate default duplication
- Add Literal types for budget and recall_tags_match validation
- Modernize type hints to X | None with from __future__ import annotations
- Add [tool.ruff] line-length = 120 to match monorepo convention
- Add py.typed PEP 561 marker
- Re-raise HindsightError before broad Exception catch
- Expand asyncio.sleep(3) comment explaining when/why it's needed
- Remove verbose from docs configure() reference table
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* perf: 3-phase retain pipeline — fix deadlocks, cap temporal links, query-time entity expansion
Major retain pipeline overhaul addressing deadlocks, write amplification,
and TimeoutErrors. Restructures retain into three phases:
Phase 1: Entity resolution on separate connection (read-heavy)
Phase 2: Core write transaction (atomic) — facts, unit_entities, links
Phase 3: Best-effort display data (error-isolated) — entity viz links, stats
Key changes:
- Sorted bulk INSERT FROM unnest() prevents deadlocks
- Temporal links capped to top-20 per unit (95% reduction)
- Batched semantic ANN via temp table + LATERAL
- Query-time entity expansion via unit_entities self-join
- Entity viz links moved to Phase 3 (post-transaction)
- HINDSIGHT_API_RETAIN_MAX_CONCURRENT config (default: 32)
* fix: increase semantic link top_k from 5 to 20
The hardcoded top_k=5 was artificially limiting semantic link creation.
Link expansion retrieval can consume up to budget (50-200) semantic
neighbors per seed set, but each fact only had 5 outgoing edges — making
the bidirectional graph very sparse.
Increasing to 20 gives retrieval 4x more edges to work with. The ANN
probe cost is unchanged (same HNSW traversal per fact, just returning
more rows). INSERT cost is negligible (~14k rows via bulk INSERT).
Also: all 18 TimeoutErrors in the latest benchmark (beam-1m-u20) were
from Gemini LLM calls, zero from the database — confirming the entity
resolution split eliminated DB timeouts entirely.
* perf: move semantic ANN search to Phase 1 to avoid transaction timeouts
The batched LATERAL ANN query (700 HNSW probes) was the last remaining
source of DB TimeoutErrors — all 29 in the latest benchmark were from
create_semantic_links_batch inside the Phase 2 write transaction.
Split semantic link creation into three phases:
- Phase 1 (separate conn, autocommit): ANN search via temp table + LATERAL.
No transaction locks, no contention with concurrent writers.
- Phase 2 (write transaction): within-batch numpy similarities (instant) +
INSERT of both within-batch and Phase 1 ANN results. No DB reads.
- Phase 3 (flush_pending_stats): future hook point for re-checking ANN
results after commit to catch links missed by concurrent batches.
Also adds 7 unit tests for compute_semantic_links_within_batch covering
empty input, identical/orthogonal embeddings, threshold filtering, top_k
cap, and tuple structure validation.
* fix: handle placeholder unit_ids in Phase 1 ANN search (not valid UUIDs)
* test: add Phase 1 ANN cross-batch test + configurable test PG port
- New test_semantic_links_phase1_ann_cross_batch verifies that the Phase 1
ANN search with placeholder unit IDs correctly creates cross-batch
semantic links after remapping to real IDs.
- Test PG port now configurable via HINDSIGHT_TEST_PG_PORT env var
(default: 5556) to avoid conflicts with running benchmark daemons.
* perf: remove retry_with_backoff from retain, set semaphore default to 4
Remove retry_with_backoff from _run_db_work and _run_delta_db_work:
- Deadlocks are prevented by sorted bulk INSERT (no need for retry)
- Transient timeouts are handled by the worker poller's task-level retry
(3 attempts, 60s spacing) which is better than rapid internal retries
that amplify I/O pressure during contention storms
Set HINDSIGHT_API_RETAIN_MAX_CONCURRENT default from 32 to 4:
- The semaphore gates Phase 1 (ANN + entity resolution) + Phase 2 (writes)
- At 4 concurrent, HNSW index I/O is manageable; at 10+ concurrent the
probes saturate disk and cause cascading timeouts
- LLM extraction still runs at full parallelism (semaphore acquired after)
* fix: add fact_type filter to Phase 1 ANN query to use per-bank HNSW indexes
The LATERAL ANN query was falling back to sequential scan + sort (90ms/probe)
because the per-bank HNSW indexes are partial indexes filtered on fact_type.
Without fact_type in the WHERE clause, PostgreSQL couldn't use them.
Fix: iterate over ('world', 'experience') and run one HNSW-indexed ANN per
type. EXPLAIN shows 8ms/probe (was 90ms) — 11x faster.
700 probes × 8ms × 2 types = ~11s total (was ~63s via seq scan).
* fix: scope temporal links by fact_type + add integration tests
Temporal links now filter by fact_type in the LATERAL query — world facts
only link to world facts, experience to experience. This matches how
retrieval filters results and avoids wasted cross-type link rows.
New integration tests:
- test_semantic_ann_uses_hnsw_index: verifies Phase 1 ANN creates
cross-batch semantic links (tests fact_type filter + placeholder remap)
- test_temporal_links_scoped_by_fact_type: verifies world facts get
temporal links to other world facts but NOT to experience facts
* fix: tolerate individual chunk LLM failures instead of failing entire batch
Changed asyncio.gather(*tasks) to asyncio.gather(*tasks, return_exceptions=True)
in both chunk-level and content-level fact extraction. A single chunk timeout
(e.g., Gemini >90s) no longer discards all other successfully extracted facts.
For a 50MB document with 17k chunks, even a 2% chunk failure rate previously
caused 0 completions (entire batch discarded). Now 16,700 facts are extracted
and only the 300 failed chunks are skipped with a warning log.
* fix: batch temporal LATERAL query for large documents (16k+ chunks)
The LATERAL query for temporal links passed all unit_ids at once into
unnest(), causing PostgreSQL timeouts on documents with 16k+ chunks.
Split into batches of 500 units per query to keep each under the
command_timeout.
Also identified: HNSW index creation on shared pg0 instances with
50k+ existing units exceeds the 60s command_timeout. This is a
test infrastructure issue (shared pg0 accumulates data) but also
affects production when creating new banks on large instances.
* feat: streaming chunk batching for large documents (RETAIN_CHUNK_BATCH_SIZE)
Process chunks in mini-batches of N (default 500), committing each batch
to the DB before starting the next. This prevents OOM kills on large
documents (50MB / 17k+ chunks) by keeping only ~500 facts + embeddings
in memory at a time instead of 50k+.
Each mini-batch goes through the full Phase 1 → 2 → 3 pipeline
independently, sharing the same document_id. On recovery (process dies
mid-way), delta retain detects already-committed chunks via content_hash
and skips them — only remaining chunks get re-extracted.
Config: HINDSIGHT_API_RETAIN_CHUNK_BATCH_SIZE (default: 500, 0 to disable)
Per-bank configurable via the hierarchical config system.
Tests:
- test_streaming_chunk_batching_produces_same_facts
- test_streaming_chunk_batching_recovery (delta retain skips committed chunks)
- test_streaming_disabled_for_small_docs
* perf(retain): producer-consumer pipeline + deferred semantic ANN
Replace the sequential streaming loop with a producer-consumer pipeline:
- LLM producer fires concurrent chunk extractions (semaphore-bounded)
- DB consumer drains queue in batches, runs Phase 1+2+3 per batch
- LLM and DB work overlap instead of running sequentially
Defer semantic links to a single final ANN pass after all batches commit:
- Remove within-batch semantic links from Phase 2 (was 2.6s/batch)
- Run parallel ANN (4 connections) after all facts committed
- top_k reduced from 50 to 20 (recall uses at most 20 neighbors)
- Recovery via operation result_metadata checkpoint
Additional optimizations:
- skip_exists_check on temporal/causal link INSERT (saves ~0.5s/batch)
- WHERE EXISTS guard on semantic link INSERT (handles document upsert)
- timeout=300s on ANN queries and bulk INSERT for large banks
- Demote [ANN] debug logs to logger.debug()
- Fix docstring typos (agent_id → bank_id)
- Fix content_index remapping in producer-consumer batches
- Fix delta retain passing contents vs delta_contents
50MB benchmark (mock LLM): 9.2 min (was 23 min) — 2.5x faster.
BEAM 10m benchmark: zero deadlocks, zero DB errors.
* refactor(retain): remove legacy fallback code paths
- Remove process_entities_batch (legacy single-connection entity processing)
- Remove extract_entities_batch_optimized (only caller was the above)
- Remove fallback entity processing inside Phase 2 transaction
- Remove legacy ANN inline fallback in create_semantic_links_batch
- Remove fallback entity_links direct-insert path in Phase 3
- Make resolved_entity_ids/entity_to_unit/unit_to_entity_ids required params
* refactor(retain): replace tuple returns with dataclasses, remove dead code
- Add EntityResolutionResult and Phase1Result dataclasses in types.py
- Replace 4-tuple return from _pre_resolve_phase1 with Phase1Result
- Remove dead `entity_links = []` variables in retain_batch and _try_delta_retain
- Remove unused `confidence_score` parameter from orchestrator.retain_batch
and _retain_batch_async_internal (was accepted but never used)
* fix(entity-resolver): remove LIKE full-scan fallbacks, use index-only trigram matching
The entity resolution query had LIKE '%...' substring conditions that bypassed
the GIN trigram index, causing full sequential scans of the entities table.
On banks with 10k+ entities, this caused TimeoutErrors (observed in BEAM 10m).
Changes:
- Remove LIKE fallbacks, use trigram % operator only (GIN index-based)
- Lower similarity threshold from 0.3 to 0.15 to catch substring relationships
- Use LOWER() on both sides for case-insensitive matching
- Migration: recreate GIN trigram index on LOWER(canonical_name)
* fix: remove schema prefix from index names in trigram migration
* fix(delta-retain): use same chunk_size as streaming path (3000 vs 120000)
_chunk_contents_for_delta defaulted to chunk_size=120000 while the streaming
path used 3000. On retry, delta re-chunked the document with different
boundaries, found 0 matching chunks, and fell through to full re-extraction.
This wasted all LLM calls on already-committed chunks.
Fix: use the same default (3000) so chunk hashes match on recovery.
* fix(retain): persist generated document_id in operation metadata for retry recovery
When no document_id is provided, retain generates a UUID. On retry, a new UUID
was generated, making delta retain and streaming chunk-hash recovery unable to
find previously committed chunks. All LLM extraction was wasted on retry.
Fix: resolve document_id early in retain_batch (before delta), persist it to
operation result_metadata, and recover it on retry. Both delta and streaming
paths now see the same document_id across attempts.
* refactor(retain): unify into single streaming pipeline, remove non-streaming path
All retains now go through the producer-consumer streaming pipeline,
regardless of document size. Small documents are processed as a single batch.
This eliminates the maintenance burden of two separate code paths.
Also fix document upsert: compare content hash to distinguish recovery
(same content, partially committed) from update (different content, needs
cascade-delete). Previously, existing chunks always triggered recovery mode.
* refactor(retain): remove dead code, replace raw dicts with Phase3Context dataclass
- Remove dead _handle_zero_facts_documents (no callers after path unification)
- Remove unused imports: defaultdict, EntityLink
- Replace raw dict phase3_context with typed Phase3Context dataclass
- Update _build_and_insert_entity_links_phase3 to use typed parameter
Remove the BFS spreading activation and MPFP (Multi-Path Fact Propagation)
graph retrieval strategies, leaving link_expansion as the sole graph
retrieval algorithm. Rename MPFPTimings to GraphRetrievalTimings and
mpfp_timings field to graph_timings since the timing struct is used by
LinkExpansionRetriever.
Deleted:
- hindsight-api-slim/hindsight_api/engine/search/mpfp_retrieval.py
- hindsight-api-slim/tests/test_mpfp_retrieval.py
Removed config: HINDSIGHT_API_MPFP_TOP_K_NEIGHBORS
* fix(deps): address critical and high severity security vulnerabilities
Bump vulnerable dependencies to patched versions across the monorepo:
Python (critical/high):
- fastmcp >=2.14.0 → >=3.2.0 (SSRF, path traversal, OAuth confused deputy, command injection)
- langchain-core >=1.2.11 → >=1.2.22 (path traversal in legacy load_prompt)
Python (low):
- cryptography >=46.0.5 → >=46.0.6 (incomplete DNS name constraint enforcement)
- pygments: add >=2.20.0 pin (ReDoS via GUID regex)
Node.js:
- serialize-javascript ^7.0.3 → ^7.0.5 (CPU exhaustion DoS)
- handlebars: add >=4.7.9 override (JS injection via AST type confusion)
- path-to-regexp: add >=0.1.13 override (ReDoS via route params)
- brace-expansion: add version range override (process hang/memory exhaustion)
Also adds type: ignore comments for FastMCP 2.x private attribute access that
ty now flags since FastMCP 3.x removed _tool_manager (guarded by try/except
and hasattr at runtime).
Regenerated all lock files across API, integrations, and tests.
* fix(deps): add ajv v8 scoped overrides for schema-utils and ajv-keywords
The global ajv ^6.14.0 override caused schema-utils and ajv-keywords to
receive ajv v6, but they require ajv v8 (for dist/compile/codegen). Add
scoped overrides to ensure these packages get ajv v8 while the global
override remains for packages that need v6.
* fix(tests): remove stateless_http from FastMCP() constructor calls
FastMCP 3.x no longer accepts stateless_http in the constructor. The
tests call tools directly without HTTP transport, so the parameter is
not needed.
* fix: update MCP tests for FastMCP 3.x _tool_manager removal
FastMCP 3.x removed _tool_manager. Tests now use
_local_provider._components for sync tool dict access and
mcp.list_tools() for async filtered tool listing.
* fix: resolve docusaurus build failures (ajv overrides + missing blog date)
- Remove global ajv ^6.14.0 override and scoped ajv-keywords/schema-utils
overrides that caused webpack compilation errors manifesting as
"Cannot read properties of undefined (reading 'date')" during SSR
and "these parameters are deprecated" warnings. Natural version
resolution (v6.12.6+ for v6 consumers, v8+ for v8 consumers) already
satisfies the security fix (>= 6.12.3).
- Add missing date frontmatter to learning-capabilities blog post.
* chore: regenerate openapi spec and docs skill
When a mental model has tags, refresh_mental_model hardcoded
tags_match="all_strict", causing empty results when most memories
are untagged. Add configurable tags_match and tag_groups fields
to MentalModelTrigger so users can control refresh filtering.
- Add tags_match (any/all/any_strict/all_strict) to override default
- Add tag_groups for compound boolean tag expressions during refresh
- Default behavior unchanged (all_strict when tags present)
- Update both refresh paths (task-based and direct)
- Add UI controls in Create/Update mental model dialogs
- Regenerate OpenAPI spec and client SDKs
CI failures are unrelated to this PR:
- test_mental_models_dimension_change_empty_table: database OID error (infrastructure flake)
- test_reflect_searches_mental_models_when_available: LLM-dependent assertion (flaky)
* feat: add optional LiteLLM SDK embedding output dimensions
Allow configuring an optional output dimension for litellm-sdk embeddings and pass it through only when set, while preserving default behavior.
Made-with: Cursor
* test: assert wrapped init error for invalid dimensions
Add a LiteLLM SDK embeddings test that verifies invalid OpenAI dimensions fail during initialize() and preserve provider error details in the wrapped RuntimeError.
Made-with: Cursor
* feat: expose document_metadata in API and control plane
Add document_metadata (sourced from retain_params.metadata) to both
list and get document endpoints. Display it in the control plane
documents table and detail panel. Drop the unused metadata column
from the documents table (was always stored as empty {}).
* fix: code review fixes for document_metadata feature
- Remove unnecessary `import json as _json` (json already imported at module level)
- Simplify redundant truthiness checks in retain_params parsing
- Regenerate OpenAPI spec and client SDKs (Python, TypeScript, Go)
- Add tests for document_metadata in get_document and list_documents
* feat(ui): improve documents table and detail panel
- Relative timestamps with full date on hover
- Remove context column from table
- Metadata shown as k=v badges (blue, like tags)
- Size in bytes instead of chars
- Document IDs wrap instead of truncating
- Detail panel wider (560px)
- Retain params: context, event_date, metadata badges
The installer skipped settings.json entirely if it already existed,
leaving version and new config keys stale. Now merges: updates version,
adds new upstream keys, preserves user customizations.
Also fixes pre-existing typo: RERANK_URL → rerank_url in ZeroEntropy
cross-encoder.
* SEO: add title and description to all integration pages
All 17 integration docs pages were missing title and description
frontmatter, causing Docusaurus to generate unhelpful titles like
"OpenClaw | Hindsight" and pull body text as meta descriptions.
- Add keyword-rich title and description frontmatter to all integration
pages in both docs/ (current) and versioned_docs/version-0.4/
- Add scripts/check-integration-seo.mjs to enforce title + description
on all future integration pages
- Wire the check into the build script so it runs locally and in CI
* Fix missing frontmatter on docs/sdks/integrations/openclaw.md
* Regenerate docs skill after integration page SEO updates
- Retitle to match search intent: "How to Add Persistent Memory to
OpenClaw with Hindsight" targets openclaw memory/persistent memory queries
- Add intro paragraph before <!-- truncate --> so Docusaurus generates a
proper meta description instead of "TL;DR"
- Expand tags from [openclaw] to include memory, agents, persistent-memory,
knowledge-graph
* fix(llamaindex): use uuid for document_id and sync version metadata
- Replace timestamp-based document_id with uuid4 hex to prevent
collisions on rapid retains (timestamp_ms can duplicate in tight loops)
- Sync __version__ in __init__.py to match pyproject.toml (0.1.2)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(docs): pass memory to run() instead of ReActAgent constructor
LlamaIndex 0.14.x ReActAgent does not accept a memory parameter in
its constructor — it's silently dropped via **kwargs. Memory must be
passed to agent.run(memory=...) where AgentWorkflow picks it up.
Also fixes the undefined `tools` variable (now `tools=[]`).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(llamaindex): strip ReAct reasoning traces from retained assistant messages
HindsightMemory.put/aput now extracts only the final Answer: text from
assistant messages containing ReAct reasoning (Thought:/Action:/Observation:
prefixes), preventing internal reasoning traces from polluting long-term memory.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(llamaindex): fix docstring example to pass memory to run()
The HindsightMemory class docstring showed the broken pattern of passing
memory= to the ReActAgent constructor, which silently drops it. Updated
to show the correct pattern: pass memory to agent.run().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Document the new configurable base URL for the ZeroEntropy reranker
provider added in #766. Also fix a type error where RERANK_URL was
renamed to rerank_url but one usage was missed.
The automatic memory example referenced an undefined `tools` variable.
Since HindsightMemory handles retain/recall transparently, no tools
are needed — use an empty list.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(llamaindex): merge two packages into single hindsight-llamaindex
Merge `llama-index-tools-hindsight` and `llama-index-memory-hindsight` into
a single `hindsight-llamaindex` package following our naming convention.
- Rename package to `hindsight-llamaindex` (Python module: `hindsight_llamaindex`)
- Move HindsightToolSpec and HindsightMemory into the same package
- Delete `llamaindex-memory/` directory
- Add CI test job for llamaindex integration
- Update docs, blog post, and integrations.json
* fix(blog): update llamaindex blog post for merged package
- Move date to 2026-03-30
- Add HindsightMemory (automatic BaseMemory) pattern
- Fix "bank must exist first" pitfall — mission auto-creates
- Align all code examples with docs page
- Update architecture diagram to show both patterns
* fix(docs): add llamaindex/openai icons, rename Codex
- Add llamaindex.png and openai.png icons
- Rename "OpenAI Codex CLI" to "Codex" in integrations.json and docs
- Use openai.png icon for Codex integration
* feat(api): add duration_ms to audit log entries
Server-computed duration in milliseconds (started_at → ended_at) on
the list audit logs endpoint. Null when ended_at is not set.
Closes#749
* feat(api): add duration_ms to audit log entries and type audit endpoints
- Add server-computed duration_ms (started_at → ended_at) to audit log
list response. Null when ended_at is not set.
- Add typed Pydantic response models for both audit log endpoints
(list and stats) so they appear in the OpenAPI spec.
- Regenerate OpenAPI spec and all client SDKs.
Closes#749
* chore: regenerate docs skill after audit log response models
* fix(mcp): handle Claude Code GET probe and make stateless_http configurable (#751)
Claude Code v2.1.84+ sends a GET to /mcp/ before POST initialize,
which fails with 405 (stateless) or 400 (stateful). Intercept
sessionless GET requests in MCPMiddleware and return 200 OK so the
client proceeds to POST initialize.
Also make stateless_http configurable via HINDSIGHT_API_MCP_STATELESS
(default: false/stateful) instead of hardcoding true.
Closes#751
* docs: add HINDSIGHT_API_MCP_STATELESS to configuration reference
* Convert codex tool_choice test to pytest style
Follow-up to #734: replace unittest.TestCase + manual sys.path
manipulation with idiomatic pytest + @pytest.mark.asyncio,
matching the rest of the test suite.
* Fix test_hierarchical_fields_categorization for new configurable fields
Update expected count from 20 to 21 and add assertions for fields
added by recent PRs: retain_default_strategy, retain_strategies,
max_observations_per_scope, reflect_source_facts_max_tokens,
llm_gemini_safety_settings, mcp_enabled_tools.
* Add LlamaIndex doc to v0.4 versioned docs and sidebars
The LlamaIndex integration doc was added to docs/ (next version) in
#672 but not to versioned_docs/version-0.4/, causing a broken link
on the /integrations page which resolves to the latest version.
* Regenerate docs skill references
Run generate-docs-skill.sh to pick up new integration pages
(codex, llamaindex) and updated configuration docs.
* Add Codex integration doc to v0.4 versioned docs and sidebar
Same issue as LlamaIndex: doc was added to docs/ (next) but not
versioned_docs/version-0.4/, causing broken link on /integrations.
* fix(codex): cleanup dead code and add to release lifecycle
- Remove orphaned reflect() method from client.py (leftover from dropped auto-mode)
- Remove dead retainToolCalls config default (never wired through)
- Add codex to release-integration.sh valid integrations
- Add settings.json version fallback to release script
- Add codex CI test job in test.yml
- Add codex to integrations.json registry
* docs(codex): add changelog page and link from integration docs
* feat(codex): add hosted installer script (get-codex)
Add self-contained installer at hindsight.vectorize.io/get-codex that
downloads scripts from GitHub, configures hooks, and supports local/cloud
mode selection — no git clone required.
Update docs and README to use the one-liner install:
curl -fsSL https://hindsight.vectorize.io/get-codex | bash
* chore(codex): remove install.sh in favor of hosted get-codex
* fix(docs): use /next/ prefix for codex changelog link
* fix(docs): use GitHub link for codex changelog back-link
* feat(codex): add Hindsight memory integration for OpenAI Codex CLI
Hooks-based integration that gives Codex CLI long-term memory via Hindsight.
Three hooks keep memory in sync: SessionStart (daemon pre-warm), UserPromptSubmit
(recall + context injection), Stop (retain conversation to memory).
Key differences from the Claude Code integration:
- Codex transcript format: JSONL with {msg: {type, message}} (user_message/agent_message)
- No CODEX_PLUGIN_ROOT env var — install.sh writes hooks.json with absolute paths
- State stored in ~/.hindsight/codex/state/ (not CLAUDE_PLUGIN_DATA)
- No async: true in hooks (not supported by Codex)
- No SessionEnd event
- hooks.json written to ~/.codex/hooks.json with codex_hooks = true in config.toml
* fix(codex): fix transcript parser for actual Codex disk format
Codex stores sessions as rollout-*.jsonl with response_item entries:
User: {type:response_item, payload:{type:message, role:user, content:[{type:input_text, text:...}]}}
Assistant: {type:response_item, payload:{type:message, role:assistant, phase:final_answer, content:[{type:output_text, text:...}]}}
Previous parser expected an undocumented {msg:{type:user_message}} format from the Rust protocol spec
that does not match the actual on-disk storage format.
* feat(codex): add reflect mode to UserPromptSubmit hook
Add recallMode config option (default: 'recall') that switches the
UserPromptSubmit hook between:
- 'recall': existing behavior, fast raw facts list
- 'reflect': agentic synthesis loop, returns coherent prose answer
Also adds reflect() method to HindsightClient and HINDSIGHT_RECALL_MODE
env var override. Reflect uses a 25s timeout (vs 10s for recall).
* feat(codex): auto mode for recall/reflect selection
Add recallMode: 'auto' (new default) that picks the operation per-query:
- Synthesis patterns (what do you know, what's my, summarize, etc.) → reflect
- All other prompts → recall (fast, raw facts, better for code tasks)
* feat(codex): add automated test suite and finalize recall-only mode
* docs(codex): add docs page and sidebar entry for Codex CLI integration
* feat: add LlamaIndex integration for Hindsight
Add hindsight-llamaindex package providing persistent memory tools for
LlamaIndex agents via the native BaseToolSpec pattern. Includes retain,
recall, and reflect tools, a convenience factory, global config, full
test suite, docs page, blog post, and integrations.json entry.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address PR review feedback for llamaindex integration
- Fix ReActAgent API: from_tools() → constructor, chat() → await run()
- Add create_bank step to all quickstart examples
- Add production patterns section to docs (tags, error handling, bank lifecycle)
- Add memory scoping recommendation to README
- Add when-not-to-use section to blog post
- Add LlamaIndex compatibility tests (agent acceptance, FunctionTool.call)
- Fix self-hosted auth wording in cookbook notebook
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use async client methods and asyncio.run() for runnable examples
- Use await client.acreate_bank() instead of sync create_bank() to
avoid "event loop already running" errors in notebooks and async contexts
- Wrap plain Python examples in async def main() + asyncio.run(main())
so they are copy-paste runnable as scripts
- Add Jupyter notebook tip to docs showing top-level await pattern
- Bank lifecycle example in docs now uses async acreate_bank
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add async tool methods to avoid event loop conflicts
HindsightToolSpec now provides both sync and async tool implementations
using LlamaIndex's (sync_fn, async_fn) tuple pattern in spec_functions.
Async agents (ReActAgent, etc.) use aretain/arecall/areflect natively,
avoiding the "Timeout context manager should be used inside a task"
error that occurred when sync _run_async() was called from within an
active event loop.
- Add aretain_memory, arecall_memory, areflect_on_memory async methods
- Extract shared kwargs builders (_retain_kwargs, _recall_kwargs, etc.)
- spec_functions now uses tuples: [("retain_memory", "aretain_memory"), ...]
- Tests verify tools have both sync fn and async fn set
- Notebook verified end-to-end with nbclient against local Hindsight
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: remove blog post from integration PR
The blog post will be pulled in separately from its own PR.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Address PR review: add context label, document_id auto-gen, bank mission, graceful errors
- Add `retain_context` param (default: "llamaindex") as source label on retain ops
- Auto-generate `document_id` as `{session_id}-{timestamp_ms}` when not provided
- Add `retain_async` param (default: True) for non-blocking retain processing
- Add `mission` param for automatic bank creation/management on first use
- Change error handling from raising HindsightError to graceful log + return message
- Add per-operation timeout constants in _client.py
- Add `context` and `mission` fields to config.py and configure()
- Update docs: document as standalone package (not LlamaHub), new params, patterns
- Tests: 51 passing (up from 34), covering all new features
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Restructure to LlamaIndex namespace packages + add BaseMemory implementation
Tools package (llama-index-tools-hindsight):
- Restructured from hindsight_llamaindex/ to llama_index/tools/hindsight/
- Import: from llama_index.tools.hindsight import HindsightToolSpec
- Follows PEP 420 implicit namespace package convention
- Removed retain_async param (client.retain() doesn't support async_processing)
Memory package (llama-index-memory-hindsight):
- New package: llama_index/memory/hindsight/
- HindsightMemory(BaseMemory) for automatic memory
- put() auto-retains user/assistant messages to Hindsight
- get(input) auto-recalls relevant memories, prepends as system message
- Graceful error handling, bank mission management, document_id generation
- 28 unit tests passing
Both packages follow LlamaIndex community conventions for future LlamaHub submission.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add max_observations_per_scope bank config
Adds a configurable limit on the number of observations per tag scope.
When the limit is reached, consolidation only updates/deletes existing
observations — no new ones are created. Enforcement is done via a
constrained Pydantic response model (max_length on creates list) so the
LLM structurally cannot exceed the limit, plus prompt guidance.
- Config: HINDSIGHT_API_MAX_OBSERVATIONS_PER_SCOPE (-1 = unlimited)
- Reorder action execution: deletes → updates → creates
- Dynamic _ConsolidationBatchResponse with max_length constraint
- Prompt CAPACITY CONSTRAINT section when near/at limit
- Observations with no tags skip the limit entirely
- Control plane UI field + docs
* fix: strengthen max_observations tests with mock LLM + defensive truncation
- Rewrite integration tests to use MockLLM with deterministic responses
(one observation per fact) instead of relying on real LLM behavior
- Add defensive truncation in _consolidate_batch_with_llm as belt-and-
suspenders — catches LLM providers that ignore JSON schema max_length
- Tests now assert exact counts, not just upper bounds
* Add blog: How We Built a 4-Way Parallel Hybrid Search System
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Add cover image for parallel hybrid search post
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Update parallel hybrid search post date to 2026-03-27
* Set author to chrislatimer
* Update recall docs link
* review: align blog post to actual retrieval code
- Reframe as evolutionary narrative (V1 asyncio.gather → connection sharing)
- Add missing reranker section (cross-encoder + multiplicative boost scoring)
- Replace MPFP references with LinkExpansion (3-signal CTE)
- Fix SQL to match actual UNION ALL approach, explain CTE planner issue
- Fix acquire_with_retry, index types (ivfflat→HNSW), fusion code
- Remove fabricated perf numbers
- Add alpha calibration rationale and connection contention insight
* add nicoloboschi and benfrank241 as co-authors
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Nicolò Boschi <boschi1997@gmail.com>
* fix(migrations): use HINDSIGHT_API_MIGRATION_DATABASE_URL when set
Session-level advisory locks are broken when the database URL goes
through PgBouncer in transaction mode: the backend connection is
returned to the pool on COMMIT, orphaning the lock, so multiple pods
can simultaneously run migrations for the same schema.
When HINDSIGHT_API_MIGRATION_DATABASE_URL is set, use it for both
the advisory lock connection and the Alembic run. Callers should
point this at the direct PostgreSQL endpoint (bypassing the pooler)
so the session-level lock is held for the full migration duration.
* refactor(migrations): move MIGRATION_DATABASE_URL to standard config
Wire HINDSIGHT_API_MIGRATION_DATABASE_URL through HindsightConfig
instead of reading os.getenv() directly in migrations.py. Add the
field to the dataclass, from_env(), log_config(), all call sites,
.env.example, and the configuration docs page.
* fix: update test mocks for migration_database_url kwarg and regenerate docs skill
---------
Co-authored-by: Nicolò Boschi <boschi1997@gmail.com>
- Add AG2 integration doc with quick start, configuration, GroupChat example, and API reference
- Add to sidebar, versioned sidebar, and integrations hub
- Add AG2 icon
* feat: add audit log for feature usage tracking
Add full auditability for all mutating and core API operations across
HTTP, MCP, and system (worker) transports. Audit entries record raw
request/response as JSONB, timing (started_at/ended_at), action, and
transport type.
Backend:
- New audit_log table with JSONB columns for expandability without
future migrations (merge migration of 3 existing heads)
- AuditLogger with fire-and-forget writes via asyncio.create_task
- @audited decorator on 28 HTTP route handlers
- MCP tool audit wrapping for 16 auditable tools
- Worker task execution wrapped with audit_context
- List endpoint with action, transport, date range filters + pagination
- Stats endpoint with per-day counts for charting
- Configurable retention sweep (concurrent-safe DELETE)
Config (env-only, static):
- HINDSIGHT_API_AUDIT_LOG_ENABLED (default: false)
- HINDSIGHT_API_AUDIT_LOG_ACTIONS (comma-separated allowlist, empty=all)
- HINDSIGHT_API_AUDIT_LOG_RETENTION_DAYS (default: -1, keep forever)
Control Plane:
- New "Audit Logs" tab on bank configuration page
- Line chart showing request volume (today/7d/30d) with action filter
- Filterable table with action, transport, date range filters
- Paginated list with detail dialog showing raw request/response JSON
Tests:
- 13 tests covering list, filters, pagination, stats, disabled mode,
action allowlist, and ordering
* fix: split 3-way merge migration into two 2-way merges
Alembic doesn't support 3-parent merge migrations. Split into a no-op
merge of 2 heads (b1c2d3e4f5g6) followed by the audit_log table
migration merging the third head.
* fix: correct merge migration to merge actual 2 heads
The original analysis incorrectly identified 3 heads. There were only 2
(a3b4c5d6e7f8 and c8e5f2a3b4d1). Remove the unnecessary intermediate
merge migration and fix the audit_log migration to merge these 2 heads.
* fix: use 'heads' instead of 'head' in migration runner
Alembic's upgrade('head') fails when multiple heads exist (e.g. from
namespace package overlaps between hindsight-api and hindsight-api-slim).
Using 'heads' (plural) handles this gracefully by upgrading all branches.
* chore: regenerate OpenAPI spec with audit log endpoints
* chore: regenerate TypeScript client and docs skill OpenAPI spec
Python and Go clients still need regeneration (requires Docker).
* chore: regenerate all client SDKs (Python, Go, TypeScript)
Adds generated audit log API clients for Python (audit_api.py),
Go (api_audit.go), and TypeScript client type updates.
* docs: add Volcano Engine as supported LLM provider
Follow-up to #714. Add Volcano Engine (ByteDance) to the documentation:
- LLM providers grid component
- Provider list in configuration docs
- Provider example with base URL and default model
- Default model table in models page
* chore: regenerate docs skill references
* feat(openclaw): add recallInjectionPosition config to preserve prompt cache
Add configurable injection position for recalled memories to avoid
breaking prefix-based prompt caching (Anthropic/Google) when agents
have large static system prompts.
Options: 'prepend' (default, current behavior), 'append' (end of
system prompt, preserves cache), 'user' (before user message).
Closes#703
* docs(openclaw): document all plugin config flags
Add missing config options to the OpenClaw docs: recallTopK,
recallTypes, recallContextTurns, recallMaxQueryChars,
recallPromptPreamble, recallInjectionPosition, recallRoles,
retainEveryNTurns, retainOverlapTurns, and debug.
* docs(claude-code): tidy configuration reference and sync README
Add missing settings (retainMode, retainToolCalls, retainTags,
retainMetadata, embedPackagePath, llmApiKeyEnv, agentName, and
several recall options) that existed in code but not in docs.
Restructure config tables with prose introductions, clearer
descriptions, and consistent layout across both files.
* refactor(claude-code): remove recallTopK setting
Unused client-side cap — Hindsight server already controls result
count via recallBudget and recallMaxTokens.