From 6c213693e31973326e0738b3f60bfec40a430756 Mon Sep 17 00:00:00 2001 From: handnewb <61999949+handnewb@users.noreply.github.com> Date: Tue, 11 Aug 2026 18:14:26 -0300 Subject: [PATCH 1/2] fix(delta): add content-based validation to block-level operations ReplaceBlockOp and RemoveBlockOp now accept an optional expected_block field. When provided, the operation is only applied if the current block at the target index serialises to the same JSON as expected_block. This protects against LLM-computed index drift: a wrong-but-in-range index would previously silently overwrite or remove an unrelated block. The operation is now skipped with a clear reason logged, and the document stays intact. The change is backward-compatible: when expected_block is None (the default, and the only shape old LLM prompts produce), the existing range-only validation is preserved unchanged. Closes #3273. --- .../hindsight_api/engine/reflect/delta_ops.py | 36 +++++++++++++++++-- .../sdks/integrations/agent-plugin.md | 14 ++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 skills/hindsight-docs/references/sdks/integrations/agent-plugin.md diff --git a/hindsight-api-slim/hindsight_api/engine/reflect/delta_ops.py b/hindsight-api-slim/hindsight_api/engine/reflect/delta_ops.py index 4c864912bb..2d505139ab 100644 --- a/hindsight-api-slim/hindsight_api/engine/reflect/delta_ops.py +++ b/hindsight-api-slim/hindsight_api/engine/reflect/delta_ops.py @@ -73,20 +73,32 @@ class InsertBlockOp(_OpBase): class ReplaceBlockOp(_OpBase): - """Replace the block at ``index`` of an existing section.""" + """Replace the block at ``index`` of an existing section. + + When ``expected_block`` is set the operation is only applied if the block + currently at ``index`` serialises to the same JSON — protecting against + LLM-computed index drift where a wrong-but-in-range index would otherwise + silently overwrite an unrelated block (#3273). + """ op: Literal["replace_block"] = "replace_block" section_id: str index: int = Field(ge=0) block: Block + expected_block: Block | None = None class RemoveBlockOp(_OpBase): - """Remove the block at ``index`` of an existing section.""" + """Remove the block at ``index`` of an existing section. + + When ``expected_block`` is set the operation is only applied if the block + currently at ``index`` serialises to the same JSON (#3273). + """ op: Literal["remove_block"] = "remove_block" section_id: str index: int = Field(ge=0) + expected_block: Block | None = None class AddSectionOp(_OpBase): @@ -299,6 +311,11 @@ def _op_summary(op: Operation) -> dict[str, Any]: } +def _blocks_equal(a: Block, b: Block) -> bool: + """Return True when two blocks serialise to identical JSON.""" + return a.model_dump_json() == b.model_dump_json() + + def apply_operations( doc: StructuredDocument, operations: list[Operation], @@ -355,6 +372,14 @@ def skip(op: Operation, reason: str) -> None: f"index out of range: {op.index} >= {len(section.blocks)}", ) continue + # Content-based validation: when expected_block is provided, + # refuse to replace a block that doesn't match (#3273). + if op.expected_block is not None and not _blocks_equal(section.blocks[op.index], op.expected_block): + skip( + op, + f"expected_block does not match current block at index {op.index} — index drift detected", + ) + continue section.blocks[op.index] = op.block applied.append(_op_summary(op)) continue @@ -370,6 +395,13 @@ def skip(op: Operation, reason: str) -> None: f"index out of range: {op.index} >= {len(section.blocks)}", ) continue + # Content-based validation (#3273) + if op.expected_block is not None and not _blocks_equal(section.blocks[op.index], op.expected_block): + skip( + op, + f"expected_block does not match current block at index {op.index} — index drift detected", + ) + continue section.blocks.pop(op.index) applied.append(_op_summary(op)) continue diff --git a/skills/hindsight-docs/references/sdks/integrations/agent-plugin.md b/skills/hindsight-docs/references/sdks/integrations/agent-plugin.md new file mode 100644 index 0000000000..87382baf0c --- /dev/null +++ b/skills/hindsight-docs/references/sdks/integrations/agent-plugin.md @@ -0,0 +1,14 @@ + +# Agent Plugins + +Portable long-term memory for any [Agent Plugins](https://agent-plugins.org) client, powered by Hindsight. + +Agent Plugins is the vendor-neutral open standard for packaging Agent Skills + MCP servers into a single distributable plugin. Hindsight ships one plugin that every compatible client can load. + +## Quick Start + +1. Get your API key from the Hindsight Cloud dashboard. +2. Set `HINDSIGHT_API_KEY` and optionally `HINDSIGHT_BANK_ID`. +3. Install the plugin in your client through its plugin/MCP UI. + +Once installed, agents can call `recall`, `retain`, and `reflect` automatically through the bundled skill. From a1af0ad1cdddc0f829b96813ad9d285437c139f7 Mon Sep 17 00:00:00 2001 From: handnewb <61999949+handnewb@users.noreply.github.com> Date: Wed, 12 Aug 2026 09:31:59 -0300 Subject: [PATCH 2/2] chore: sync agent-plugin.md with latest generate-docs-skill.sh output --- .../sdks/integrations/agent-plugin.md | 83 +++++++++++++++++-- 1 file changed, 77 insertions(+), 6 deletions(-) diff --git a/skills/hindsight-docs/references/sdks/integrations/agent-plugin.md b/skills/hindsight-docs/references/sdks/integrations/agent-plugin.md index 87382baf0c..c47aa3256a 100644 --- a/skills/hindsight-docs/references/sdks/integrations/agent-plugin.md +++ b/skills/hindsight-docs/references/sdks/integrations/agent-plugin.md @@ -1,14 +1,85 @@ # Agent Plugins -Portable long-term memory for any [Agent Plugins](https://agent-plugins.org) client, powered by Hindsight. +Portable long-term memory for any [Agent Plugins](https://agent-plugins.org) client, powered by [Hindsight](https://vectorize.io/hindsight). -Agent Plugins is the vendor-neutral open standard for packaging Agent Skills + MCP servers into a single distributable plugin. Hindsight ships one plugin that every compatible client can load. +[Agent Plugins](https://agent-plugins.org) is the vendor-neutral open standard (developed with Amazon, Cursor, Microsoft, OpenAI, and Vercel) for packaging **Agent Skills + MCP servers** into a single distributable plugin. Instead of a separate integration per tool, Hindsight ships **one** plugin that every compatible client can load — at launch: **ChatGPT / Codex, Cursor, GitHub Copilot, Kiro, and VS Code**. ## Quick Start -1. Get your API key from the Hindsight Cloud dashboard. -2. Set `HINDSIGHT_API_KEY` and optionally `HINDSIGHT_BANK_ID`. -3. Install the plugin in your client through its plugin/MCP UI. +> **💡 Recommended: Hindsight Cloud** +> +[Sign up free](https://ui.hindsight.vectorize.io/signup) for a Hindsight Cloud API key — no self-hosting, no local daemon to manage. +1. Get your `hsk_...` API key from [ui.hindsight.vectorize.io/connect](https://ui.hindsight.vectorize.io/connect). +2. Set the environment variables the plugin reads: -Once installed, agents can call `recall`, `retain`, and `reflect` automatically through the bundled skill. + ```bash + export HINDSIGHT_API_KEY="hsk_your_token" + export HINDSIGHT_BANK_ID="my-project" # optional; defaults to "default" + ``` + +3. Install the plugin in your client (through its plugin/MCP UI, or by pointing it at the plugin directory — installation is client-specific per the standard). + +Once installed, ask the agent something that depends on past context, or tell it a durable preference — it calls `recall` and `retain` automatically, guided by the bundled skill. + +## What's in the plugin + +The plugin is a thin, transport-only wrapper — all memory logic stays server-side in Hindsight. It follows the Agent Plugins `1.0.0` layout: + +``` +agent-plugin/ +├── plugin.json # manifest ($schema + name + metadata) +├── mcp.json # Hindsight MCP server (Streamable HTTP) +└── skills/ + └── hindsight-memory/ + └── SKILL.md # teaches the agent when to recall / retain / reflect +``` + +- **`mcp.json`** connects the client to Hindsight's built-in [MCP server](../../developer/mcp-server.md) over Streamable HTTP. +- **`skills/hindsight-memory/SKILL.md`** is loaded into the agent's context so it knows *when* to reach for memory, not just that the tools exist. + +## Memory tools + +Via the MCP server, the agent gets Hindsight's full memory surface. The three it reaches for most: + +| Tool | When | What it does | +|------|------|--------------| +| `recall` | Before answering, when past context could help | Semantic + keyword + graph + temporal retrieval over the bank | +| `retain` | After learning a durable, reusable fact | Stores the fact for future sessions | +| `reflect` | When a lookup is too shallow and you need synthesized reasoning | Disposition-aware reasoning over everything remembered | + +Additional tools (knowledge pages, mental models, documents, tags) are exposed too — see the [MCP Server reference](../../developer/mcp-server.md). + +## Configuration + +The plugin reads two environment variables, interpolated into `mcp.json`: + +| Setting | Env Var | Default | Description | +|---------|---------|---------|-------------| +| API key | `HINDSIGHT_API_KEY` | — | Your `hsk_...` key. Sent as `Authorization: Bearer`. Required for Hindsight Cloud. | +| Memory bank | `HINDSIGHT_BANK_ID` | `default` | Bank to read from and write to (sent as `X-Bank-Id`). Use one bank per user, project, or team for isolation. | + +> **📝 Env-var syntax varies by client** +> +Most clients substitute `${VAR}`; some (VS Code, Cursor) use `${env:VAR}`. If your client doesn't interpolate, paste the literal key and bank id into `mcp.json`. +**Self-hosting:** replace the host in `mcp.json` (`https://api.hindsight.vectorize.io`) with your deployment's URL. A local server with the MCP endpoint open needs no API key. + +## Explicit tools vs. automatic capture + +Agent Plugins `1.0.0` standardizes **Skills + MCP**, not session lifecycle hooks. This plugin therefore delivers **explicit, tool-driven** memory that works identically across every supported client. + +For the fully automatic experience — recall injected before every prompt and transcripts retained on session end — use the native, hook-based integration built for your specific tool, such as [Claude Code](claude-code.md) or [Codex](codex.md). Both share the same Hindsight banks, so memory captured by the hook-based integration is recalled through the Agent Plugin, and vice versa. + +## Troubleshooting + +**No memories recalled**: `recall` returns results only after something has been retained. Retain a fact first, or seed the bank via the [API](../../developer/api/quickstart.md). + +**401 Unauthorized**: Check `HINDSIGHT_API_KEY` is set and your client is interpolating it into the `Authorization` header (see the env-var syntax note above). + +**Wrong or empty memory**: Confirm `HINDSIGHT_BANK_ID` points at the bank you expect. Different tools writing to different banks won't share memory. + +## Learn more + +- [Agent Plugins standard](https://agent-plugins.org) +- [Hindsight MCP Server reference](../../developer/mcp-server.md) +- [Hindsight Cloud sign-up](https://ui.hindsight.vectorize.io/signup)