DK09876 91d767cdcb feat(cursor): add Hindsight memory plugin for Cursor (#866)
* feat(cursor): add Hindsight memory plugin for Cursor

Adds a complete Cursor integration using the plugin architecture
(hooks, skills, rules). Automatically recalls relevant memories
before each prompt and retains conversation transcripts on task
completion. Modeled after the claude-code integration with
Cursor-specific adaptations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs(cursor): add integration docs, blog post, and sidebar entry

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs(cursor): clarify plugin vs MCP modes, add hook diagnostics

- Add plugin-vs-MCP comparison table near top of integration doc
- Add "Verifying Plugin Hooks" section with state file commands
- Add troubleshooting note: visible tool calls = MCP, not plugin
- Write last_retain.json state file in retain.py for diagnostics
- Add mode: plugin and query_length to recall state file
- Fix test_settings_file_loaded to isolate from user config

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(cursor): install path, always-write diagnostics, Cloud snippets

- Add mkdir -p before cp -r in all install examples (first-run fix)
- Add "fully quit and reopen Cursor" note to all setup flows
- Recall/retain hooks now write status on every invocation
  (success, empty, skipped, error) not just on success
- Fix docs to show ~/.hindsight/cursor-state/ default path
- Add concrete Hindsight Cloud config snippet to Quick Start
- Add Cloud option to blog post setup section

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(cursor): add session field to dynamic bank IDs, add changelog

- Support "session" in dynamicBankGranularity for per-conversation banks
- Add changelog page for cursor integration
- Add test for session-based dynamic bank ID

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(cursor): sync integration README with cookbook/blog setup guidance

- Add mkdir -p for plugin install path
- Add "fully quit and reopen Cursor" instruction
- Show Cloud as Option A, local as Option B, daemon as Option C
- Match the setup flow documented in the cookbook and blog

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(cursor): add pip/uvx installer, fix review findings

- Add hindsight_cursor package with CLI `init` and `uninstall` commands
- Add pyproject.toml for PyPI publishing via existing release pipeline
- Update README install path: `pip install hindsight-cursor && hindsight-cursor init`
- Fix rule/skill files to describe plugin behavior instead of MCP tools
- Add diagnostics on get_api_url failure paths in both hooks
- Remove missing assets/avatar.png reference from plugin manifest
- Add Cloud token retrieval guidance (Settings > API Keys)
- Add test_cli.py with 8 tests for init/uninstall commands

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(cursor): daemon timeout, config defaults, full config docs

- Set daemonIdleTimeout default to 300s (was 0/infinite with no cleanup hook)
- Fix retainEveryNTurns fallback from 1 to 10 in retain.py
- Fix DEFAULTS: hindsightApiUrl="" and bankId="cursor" to match settings.json
- Document all config settings in README (was missing ~15 entries)
- Fix pytest version discrepancy in pyproject.toml
- Fix plugin.json author to "Vectorize" for consistency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs(cursor): streamline setup with init flags, add Docker instructions

- Restructure Quick Start around Cloud vs Local as two clear paths
- Use hindsight-cursor init --api-url/--api-token for one-command setup
- Add Docker run command for users without a local Hindsight server
- Remove separate "configure" step that contradicted init behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(cursor): replace beforeSubmitPrompt with sessionStart + MCP

beforeSubmitPrompt does not support additionalContext in Cursor's hook
system — the old recall.py was silently ignored. This rewrites the
architecture to use Cursor's native mechanisms:

- sessionStart hook for ambient project-level recall (supports additionalContext)
- MCP integration for on-demand recall/retain/reflect tools mid-session
- stop hook for auto-retain (unchanged, works correctly)

Also fixes Python floor (3.9 -> 3.10, pytest 9 requires it) and
updates docs/blog to match the new architecture.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(cursor): workaround broken sessionStart additionalContext

Cursor's sessionStart hook accepts additionalContext output but silently
drops it before the agent's composer handle is ready — a race condition
acknowledged by Cursor staff in 2026-04, still present in 3.6.31
(verified 2026-06-02 with a marker-emitting test hook). Without a
workaround the plugin's "auto-recall memories at session start" feature
silently does nothing in every install.

Per Cursor staff guidance (Dean Rie, thread 158452), the documented
escape hatch is to write a workspace .cursor/rules/<file>.mdc with
alwaysApply: true — the rules engine injects those reliably. Plugin-
local rules dirs (~/.cursor/plugins/local/...) are NOT reliable per
thread 159101.

Implementation:

- scripts/lib/rules_file.py (new): owns the workaround. Three helpers:
    * rotate_session_rules() — deletes any prior rules file at the top
      of each sessionStart so an empty recall doesn't leave stale
      memories from a previous session.
    * write_session_rules() — writes the .mdc with alwaysApply: true,
      an HTML comment that explains what the file is and links to the
      Cursor bug, and the recalled memories inside a
      <hindsight_memories> block (same wrapper the broken native path
      used, so the static rules guidance is unchanged).
    * ensure_gitignored() — idempotently appends the file path to
      <workspace>/.gitignore when the workspace is a git repo. No-ops
      otherwise. Matches both /-anchored and bare relative forms so we
      don't double-add against an existing entry.

- scripts/session_start.py: rotates at the top, writes the fallback
  file after recall succeeds, gates both behind config flags
  (useRulesFileFallback, appendToGitignore, both default True). Still
  emits additionalContext to stdout below — when Cursor fixes the
  upstream bug, dropping the workspace write is the only code change
  needed; the same plugin works on the native path with no protocol
  rev.

- scripts/lib/config.py: two new config keys + HINDSIGHT_USE_RULES_
  FILE_FALLBACK / HINDSIGHT_APPEND_TO_GITIGNORE env overrides.

- rules/hindsight-memory.mdc: tells the agent where recalled memories
  now appear (the new .cursor/rules/hindsight-session.mdc file) and
  notes that the file is plugin-generated and safe to delete.

- tests/test_rules_file.py: 18 tests pinning the on-disk shape:
  frontmatter, alwaysApply, bug link, rotation, idempotent gitignore
  with both anchor forms, falsy workspace handling, write-error
  degradation.

Why this design (vs. alternatives):

- Just shipping MCP-only and documenting the limitation would repeat
  the OpenAI Agents notebook-10 Pattern-1 failure mode: the agent has
  to choose to call recall, and small models reliably skip it. Auto-
  inject doesn't depend on tool-call choice.
- Reverting to beforeSubmitPrompt would mean a recall per turn instead
  of per session, and Cursor staff have signalled additional_context
  on that hook is unimplemented (forum 150707).
- The workspace file is the price of Cursor's bug being open with no
  ETA. Mitigations: auto-rotate, auto-gitignore, in-file explanatory
  comment, config opt-outs.

Verification:

- Full suite: 74 passed (56 prior + 18 new).
- Smoke end-to-end against a fresh git repo: rules file written with
  correct frontmatter, .gitignore appended cleanly with both an
  explanatory comment and the path entry, no duplicate-add on re-run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(cursor): adopt requires_real_llm bucketing + live E2E + lockfile + docs

Aligns cursor with the standing test-bucketing convention from PR #1469
("Split test suite into deterministic mock and real LLM buckets") that the
other eight Python integrations already follow.

Changes:

- pyproject.toml: register the `requires_real_llm` marker so the live
  E2E suite is selectable as a discrete bucket (and excluded from the
  deterministic CI path via `pytest -m "not requires_real_llm"`). Add
  hindsight-client as a dev dep — the E2E driver needs it to seed and
  verify banks; the runtime plugin scripts still use stdlib only.

- tests/test_e2e.py (new): four-test gated suite that drives the actual
  hook scripts the way Cursor does — JSON on stdin, env vars for config
  — against a live Hindsight server. Covers:
    1. session_start writes the rules-file workaround with recalled
       content, appends `.gitignore`, and emits the forward-compat
       `additionalContext` to stdout.
    2. empty-bank case: hook succeeds without writing a rules file.
    3. opt-out: `useRulesFileFallback=false` produces no `.cursor/` or
       `.gitignore` mutations even when recall surfaces content.
    4. retain end-to-end: drives `retain.py` with a JSONL transcript
       (the on-disk shape Cursor actually emits, not an inline messages
       array), then verifies the bank holds the fact via direct recall.

  Two non-obvious fixtures the suite needs:
  - `HOME` / `CURSOR_PLUGIN_DATA` redirected to tmp so the test doesn't
    touch the developer's real `~/.hindsight/cursor.json` or state.
  - `HINDSIGHT_BANK_MISSION` overridden to a focused mission that aligns
    with the seeded fixtures — the production default mission is broad
    boilerplate, fine for real users but too diffuse to reliably
    surface targeted test content within a deadline.
  - `HINDSIGHT_RETAIN_EVERY_N_TURNS=1` because retain.py batches every
    N turns (10 by default) and a single-shot test only has one turn.

- uv.lock: committing per the convention every other Python
  integration follows. 258 KB, 29 packages resolved, `uv lock --check`
  clean.

- README.md: new "How session memory reaches the agent" section
  documenting why the plugin writes `<workspace>/.cursor/rules/
  hindsight-session.mdc` (Cursor's native `additionalContext` channel
  is broken, forum thread 158452, still open in 3.6.31). Captures the
  empirically-verified behaviour: Cursor blocks prompt submission
  until sessionStart returns, so every new agent's first prompt has
  memories, the rules file is regenerated each session, and the file
  is auto-gitignored. Two new config knobs (`useRulesFileFallback`,
  `appendToGitignore`) added to the Session Recall table.

Verification:
- Deterministic bucket: 74 pass / 4 deselected (the new gated E2E).
- Live bucket (HINDSIGHT_API_URL=http://127.0.0.1:8888): 4 pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(cursor): default to hosted backend + give each retain a distinct document_id

V2 audit (2026-06-02) caught two real bugs in cursor that were missed by
the V1 pass:

1) Goal-5 (Default to Cloud) FAIL — settings.json shipped
   hindsightApiUrl='' and the daemon path treated empty as "fall back to
   local daemon at 127.0.0.1:9077". Users following the docs ("just enable
   the plugin") never reached the hosted backend without explicitly
   passing --api-url. Every other integration's empty-config path lands on
   https://api.hindsight.vectorize.io.

2) The retain path used document_id=session_id in full-session mode,
   which silently upserts the same Hindsight document on every retain.
   The audit's 5-turn distinct-fact driver exposed this as "5-turn cloud
   → 1 topic surfaced" — earlier turns got overwritten because each
   retain rewrote the single per-session document with whatever
   transcript snapshot was current.

Both are addressed below; the live test suite still passes against the
local server and the new deterministic tests pin the cloud-default
resolution + the unique-document-id derivation.

Changes:

- scripts/lib/config.py — add ``DEFAULT_HINDSIGHT_API_URL`` constant
  (``https://api.hindsight.vectorize.io``). Add ``useLocalDaemon`` flag
  (default ``False``) so self-hosters can opt back into the auto-managed
  daemon path. New env override ``HINDSIGHT_USE_LOCAL_DAEMON``.

- scripts/lib/daemon.py — rewrite ``get_api_url`` resolution:
    1. Explicit ``hindsightApiUrl`` wins.
    2. A locally-running server on the configured port is used (preserves
       the "developer already started a daemon" path).
    3. ``useLocalDaemon=True`` AND ``allow_daemon_start=True`` (retain
       path) triggers the auto-managed daemon. Recall path never starts a
       daemon on its own.
    4. Otherwise → ``DEFAULT_HINDSIGHT_API_URL``. A failed daemon-start
       under (3) also falls back here rather than hard-erroring, so the
       plugin keeps working when ``hindsight-embed`` isn't on PATH.

- scripts/retain.py — every retain now derives
  ``document_id = f"{session_id}-{int(time.time() * 1000)}"`` regardless
  of retainMode. The chunked-vs-full-session distinction at the doc-id
  layer was always a misfeature; full-session mode now means "the
  transcript ingested per retain may span the whole session", not "every
  retain writes the same document".

- tests/test_daemon.py (new) — pin the four-tier resolution + env
  override + the source-shape of retain.py's document_id derivation.

Verification:
- Deterministic bucket: 81 pass / 4 deselected (74 prior + 7 new).
- Live bucket: 4 pass / 0 fail against 127.0.0.1:8888.
- Manual smoke for empty-config → returns ``DEFAULT_HINDSIGHT_API_URL``.
- Live server still resolves to ``http://127.0.0.1:8888`` when healthy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(cursor): parse Cursor 3.x role-nested transcript format

retain.py's read_transcript only recognized two transcript shapes:
- Flat:        {role, content}
- Type-nested: {type: "user"|"assistant", message: {role, content}}

Cursor 3.6.31 writes a third shape to its stop-hook transcript:

  {"role":"user","message":{"content":[
    {"type":"text","text":"..."},
    {"type":"tool_use","name":"...","input":{...}}
  ]}}

Top-level has `role` (not `type`), and `content` lives under `message`
as a list of typed blocks (not at the top level as a string). The old
parser's two branches both missed every line: `entry.get("type")` was
None and `"content" in entry` was False. read_transcript silently
returned [] for every Cursor 3 transcript, and retain.py bailed with
status=skipped reason=empty_transcript on every stop hook.

Visible symptom: auto-retain silently stops working under Cursor 3
even though the stop hook fires correctly and transcript_path points
at a real, populated file (verified by reading
~/Library/Application Support/Cursor/logs/.../cursor.hooks.*.log —
the input JSON includes a valid transcript_path that the parser then
ignores). End users see recall continue to work (sessionStart writes
the rules-file workaround) but new turns never get retained.

Fix:
- Add _normalize_blocks_to_text to flatten typed-block lists to a
  single string, inlining a compact [tool_use:<name>] marker so
  downstream Answer:/Thought: handling still sees coherent structure.
- Recognize the role-nested Cursor 3 shape explicitly.
- Keep flat and type-nested handling intact.

Verified end-to-end against a real Cursor 3.6.31 transcript captured
from ~/.cursor/projects/.../agent-transcripts/<conv>/<conv>.jsonl:
read_transcript now returns the 15 messages it should (1 user + 14
assistant turns) instead of 0.

Regression tests (3 added):
- test_read_transcript_parses_flat_format pins the flat shape.
- test_read_transcript_parses_type_nested_format pins the type-nested
  shape.
- test_read_transcript_parses_cursor3_role_nested_with_block_content
  is the regression: fails on the pre-fix parser (returns []), passes
  now. Also asserts the [tool_use:Shell] marker survives.

14/14 tests in test_hooks.py pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(docs): drop missing image refs in cursor blog post

The 2026-04-03 cursor-persistent-memory blog references
/img/blog/cursor-persistent-memory.png in both frontmatter and
inline markdown, but the image was never added to the repo. build-docs
fails MDX compilation with "Markdown image with URL
/img/blog/cursor-persistent-memory.png couldn't be resolved to an
existing local image file".

Strip the two references so the post renders. The prose stands on its
own without an illustration; an image can be added in a follow-up PR
if/when one is produced.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(cursor): sync openapi.json with main

Branch carried an older snapshot of hindsight-docs/static/openapi.json
that pre-dated main's addition of the OperationProgress schema.
check-openapi-compatibility flagged the missing 'progress' field on
GET /v1/default/banks/{bank_id}/operations/{operation_id} as a
backwards-incompatible removal.

Re-checkout main's openapi.json onto the branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(cursor): drop cursor-persistent-memory blog post

The blog post was added as marketing for the Cursor integration but
the accompanying illustration was never produced. Earlier commit
0e4b2568 stripped the missing image references so build-docs would
pass; user prefers the blog post itself be dropped from the integration
PR and authored separately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(cursor): ruff format scripts + generate docs-skill changelog

verify-generated-files CI flagged drift in three cursor scripts
(scripts/lib/daemon.py, scripts/retain.py, scripts/session_start.py)
and a missing skills/hindsight-docs/.../integrations/cursor.md.

- scripts: applied ruff format/check (3 files reformatted, all checks
  pass).
- generate-docs-skill.sh produced the integrations/cursor.md changelog
  mirror.

Format-only + a generated file regeneration; no behaviour changes.
All cursor tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ci: re-trigger CI

A previous push to this branch silently did not trigger a pull_request
event in GitHub Actions, leaving the PR without a CI run for the latest
HEAD. Push an empty commit to force a new event.

* ci: empty commit to attach pull_request CI check to the PR head

(Previous pushes did not auto-trigger pull_request workflow events for
reasons internal to GitHub Actions; manual workflow_dispatch runs passed
green but their checks don't roll up onto the PR. Re-poking the head
to surface the green state on the PR.)

* ci: trailing newline to force CI retrigger

* fix(cursor): address review — drop dead code, register changelog + gallery

- Remove compose_recall_query / truncate_recall_query from scripts/lib/content.py
  (ported from openclaw but unused — cursor only recalls at sessionStart) and
  their test; slice_last_turns_by_user_boundary stays (used by retain.py).
- Add cursor to the INTEGRATIONS map in generate_changelog.py so the release
  changelog step resolves the slug.
- Add the integrations.json gallery entry + icon and rely on the existing
  docs-integrations/cursor.md so check-integrations.mjs passes.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Ben <ben.bartholomew@vectorize.io>
Co-authored-by: DK09876 <dk09876@DK09876s-MacBook-Pro.local>
2026-06-09 09:50:22 -07:00
2025-12-03 11:52:25 +01:00
2026-06-09 14:55:08 +02:00
2026-06-09 14:55:08 +02:00
2026-06-09 14:55:08 +02:00
2026-06-09 14:55:08 +02:00
2026-06-09 14:55:08 +02:00
2026-06-09 14:55:08 +02:00
2026-06-09 14:55:08 +02:00
2026-06-09 14:55:08 +02:00
2026-06-09 14:55:08 +02:00
2025-12-03 21:11:27 +01:00
2025-10-30 12:53:12 +01:00
2025-11-14 14:07:41 +01:00
2025-12-04 10:20:26 +01:00
2025-12-11 12:46:48 +01:00
2025-12-03 23:06:15 +01:00
2026-06-09 14:55:08 +02:00
2025-12-04 10:20:26 +01:00
2026-06-09 14:55:08 +02:00


What is Hindsight?

Hindsight™ is an agent memory system built to create smarter agents that learn over time. Most agent memory systems focus on recalling conversation history. Hindsight is focused on making agents that learn, not just remember.

It eliminates the shortcomings of alternative techniques such as RAG and knowledge graph and delivers state-of-the-art performance on long term memory tasks.

Memory Performance & Accuracy

Hindsight is the most accurate agent memory system ever tested according to benchmark performance. It has achieved state-of-the-art performance on the LongMemEval benchmark, widely used to assess memory system performance across a variety of conversational AI scenarios. The current reported performance of Hindsight and other agent memory solutions as of January 2026 is shown here:

Overview

The benchmark performance data for Hindsight has been independently reproduced by research collaborators at the Virginia Tech Sanghani Center for Artificial Intelligence and Data Analytics and The Washington Post. Other scores are self-reported by software vendors.

Hindsight is being used in production at Fortune 500 enterprises and by a growing number of AI startups.

Adding Hindsight to Your AI Agents

The easiest way to use Hindsight with an existing agent is with the LLM Wrapper. You can add memory to your agent with 2 lines of code. That will swap your current LLM client out with the Hindsight wrapper. After that, memories will be stored and retrieved automatically as you make LLM calls.

If you need more control over how and when your agent stores and recalls memories, there's also a simple API you can integrate with using the SDKs or directly via HTTP.

Hindsight Banner


🤖 Using a coding agent? Install the Hindsight documentation skill for instant access to docs while you code:

npx skills add https://github.com/vectorize-io/hindsight --skill hindsight-docs

Works with Claude Code, Cursor, and other AI coding assistants.


Quick Start

export OPENAI_API_KEY=sk-xxx

docker run -it --pull always --name hindsight --restart unless-stopped -p 8888:8888 -p 9999:9999 \
  -e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \
  -v hindsight-data:/home/hindsight/.pg0 \
  ghcr.io/vectorize-io/hindsight:latest

API: http://localhost:8888 UI: http://localhost:9999

You can modify the LLM provider by setting HINDSIGHT_API_LLM_PROVIDER. Valid options are openai, anthropic, gemini, groq, ollama, lmstudio, and minimax. The documentation provides more details on supported models.

Docker (external PostgreSQL)

export OPENAI_API_KEY=sk-xxx
export HINDSIGHT_DB_PASSWORD=choose-a-password
cd docker/docker-compose
docker compose up 

Oracle AI Database is also supported for enterprise deployments with full feature parity. See the storage documentation for details.

API: http://localhost:8888 UI: http://localhost:9999

Client

pip install hindsight-client -U
# or
npm install @vectorize-io/hindsight-client

Python

from hindsight_client import Hindsight

client = Hindsight(base_url="http://localhost:8888")

# Retain: Store information
client.retain(bank_id="my-bank", content="Alice works at Google as a software engineer")

# Recall: Search memories
client.recall(bank_id="my-bank", query="What does Alice do?")

# Reflect: Generate disposition-aware response
client.reflect(bank_id="my-bank", query="Tell me about Alice")

Node.js / TypeScript

npm install @vectorize-io/hindsight-client
const { HindsightClient } = require('@vectorize-io/hindsight-client');

const main = async () => {
  const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });

  await client.retain('my-bank', 'Alice loves hiking in Yosemite');

  const results = await client.recall('my-bank', 'What does Alice like?');
  console.log(results);
}

main();

Python Embedded (no server required)

pip install hindsight-all -U
import os
from hindsight import HindsightServer, HindsightClient

with HindsightServer(
    llm_provider="openai",
    llm_model="gpt-5-mini", 
    llm_api_key=os.environ["OPENAI_API_KEY"]
) as server:
    client = HindsightClient(base_url=server.url)
    client.retain(bank_id="my-bank", content="Alice works at Google")
    results = client.recall(bank_id="my-bank", query="Where does Alice work?")

Use Cases

Hindsight is built to support conversational AI agents as well as agents that are intended to perform tasks autonomously. The ideal use case for Hindsight are agents that require a blend of these features such as AI employees that need to handle open-ended tasks, change behavior based on user feedback, and learn to perform complex tasks to automate work at a level that approximates a human work. Hindsight can be used with simple AI workflows like those built with n8n and other similar tools, but may be overkill for such applications.

Per-User Memories and Chat History

One of the simpler use cases you can use Hindsight for is to personalize AI chatbots and other conversational agents by storing and recalling memories associated with individual users.

The requirements for this use case usually look something like this:

Per-User Memories

Satisfying these requirements in Hindsight is straightforward. When new user inputs and tool calls are ingested into Hindsight using the retain operation, custom metadata can be used to enrich the new memories. Metadata provides a convenient way to isolate memories that need to be restricted to a given user. Once these are fed into the retain operation, any raw memories and mental models that get created can be filtered when retrieving relevant memories.

Per-User Memories


Architecture & Operations

Overview

Most agent memory implementations rely on basic vector search or sometimes use a knowledge graph. Hindsight uses biomimetic data structures to organize agent memories in a way that is more like how human memory works:

  • World: Facts about the world ("The stove gets hot")
  • Experiences: Agent's own experiences ("I touched the stove and it really hurt")
  • Mental Models: Learned understanding of the agent's world formed by reflecting on raw memories and experiences.

Memories in Hindsight are stored in banks (i.e. memory banks). When memories are added to Hindsight, they are pushed into either the world facts or experiences memory pathway. They are then represented as a combination of entities, relationships, and time series with sparse/dense vector representations to aid in later recall.

Hindsight provides three simple methods to interact with the system:

  • Retain: Provide information to Hindsight that you want it to remember
  • Recall: Retrieve memories from Hindsight
  • Reflect: Reflect on memories and experiences to generate new observations and insights from existing memories.

Retain

The retain operation is used to push new memories into Hindsight. It tells Hindsight to retain the information you pass in as an input.

from hindsight_client import Hindsight

client = Hindsight(base_url="http://localhost:8888")

# Simple
client.retain(
    bank_id="my-bank",
    content="Alice works at Google as a software engineer"
)

# With context and timestamp
client.retain(
    bank_id="my-bank",
    content="Alice got promoted to senior engineer",
    context="career update",
    timestamp="2025-06-15T10:00:00Z"
)

Behind the scenes, the retain operation uses an LLM to extract key facts, temporal data, entities, and relationships. It passes these through a normalization process to transform extracted data into canonical entities, time series, and search indexes along with metadata. These representations create the pathways for accurate memory retrieval in the recall and reflect operations.

Retain Operation

Recall

The recall operation is used to retrieve memories. These memories can come from any of the memory types (world, experiences, etc.)

from hindsight_client import Hindsight

client = Hindsight(base_url="http://localhost:8888")

# Simple
client.recall(bank_id="my-bank", query="What does Alice do?")

# Temporal
client.recall(bank_id="my-bank", query="What happened in June?")

Recall performs 4 retrieval strategies in parallel:

  • Semantic: Vector similarity
  • Keyword: BM25 exact matching
  • Graph: Entity/temporal/causal links
  • Temporal: Time range filtering

Retain Operation

The individual results from the retrievals are merged, then ordered by relevance using reciprocal rank fusion and a cross-encoder reranking model.

The final output is trimmed as needed to fit within the token limit.

Reflect

The reflect operation is used to perform a more thorough analysis of existing memories. This allows the agent to form new connections between memories and build a more thorough understanding of its world.

For example, the reflect operation can be used to support use cases such as:

  • An AI Project Manager reflecting on what risks need to be mitigated on a project.
  • A Sales Agent reflecting on why certain outreach messages have gotten responses while others haven't.
  • A Support Agent reflecting on opportunities where customers have questions not answered by current product documentation.

The reflect operation can also be used to handle on-demand question answering or analysis which require more deep thinking.

from hindsight_client import Hindsight

client = Hindsight(base_url="http://localhost:8888")

client.reflect(bank_id="my-bank", query="What should I know about Alice?")

Retain Operation


Resources

Documentation:

Clients:

Community:


Star History

Star History Chart

Contributing

See CONTRIBUTING.md.

License

MIT — see LICENSE


Built by Vectorize.io

S
Description
Complete Hindsight documentation for AI agents. Use this to learn about Hindsight architecture, APIs, configuration, and best practices.
Readme MIT 799 MiB
Languages
Python 73%
TypeScript 16.3%
MDX 5.5%
Rust 1.9%
JavaScript 1.2%
Other 2.1%