Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,53 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

## [Unreleased]

## [0.9.5] — 2026-05-09

Bug-fix patch focused on **search recall correctness** and **plugin compatibility**. Pins `iii-engine` to v0.11.2 because v0.11.6 ships a regression that breaks the agentmemory worker. Adds a hard guard against silent vector-index corruption, fixes BM25 indexing for memories saved via `memory_save`, and lands four Hermes plugin fixes that make the memory provider actually usable end-to-end.

If you've been seeing `memory_smart_search` return empty results for memories you just saved, this release fixes that. If you've been hitting `hermes memory status` reporting "not available" against a healthy systemd-managed install, this release fixes that too.

### Fixed

- **BM25 search now indexes memories saved via `memory_save`.** `mem::remember` was writing to `KV.memories` but never calling `getSearchIndex().add()`, so `memory_smart_search` and `memory_recall` returned empty for everything saved through that path — for **every** version since v0.9.0. Synthesizes a `CompressedObservation` from the saved Memory (title + content + concepts + files) and adds it to BM25 right after the durable write. `rebuildIndex()` now walks `KV.memories` so a fresh rebuild covers the full corpus, and a startup backfill retroactively indexes pre-existing memories on first start after upgrade — no manual reindex required. New `SearchIndex.has(id)` is the idempotency gate. (#258, closes [#257](https://github.com/rohitg00/agentmemory/issues/257) — thanks @Nizar-BenHamida for the precise repro and log capture)

- **Embedding providers no longer silently corrupt the vector index when an API returns wrong-dimension vectors.** `cosineSimilarity` returns `0` on length mismatch instead of throwing, so a wrong-size vector got stored, never matched anything, and the corresponding memory became invisible without a single log line. `withDimensionGuard()` now wraps every embedding provider at the factory boundary in `src/providers/embedding/index.ts` — `embed()`, `embedBatch()` (per-vector, indexed errors like `embedBatch[3]`), and `embedImage()` all throw a descriptive error when the returned `Float32Array` length doesn't match `provider.dimensions`. The persistence-restore path got the same defense: `IndexPersistence.load()` now refuses to start when persisted vectors mismatch the active provider, with an actionable error spelling out the recovery paths (re-embed / `AGENTMEMORY_DROP_STALE_INDEX=true` / switch back). (#248, closes [#247](https://github.com/rohitg00/agentmemory/issues/247) and [#256](https://github.com/rohitg00/agentmemory/issues/256) — thanks @AmmarSaleh50 for the issue analysis, the fix PR, and the test coverage)

- **Hermes plugin: `handle_tool_call` now returns JSON strings, not raw Python dicts.** Hermes stores the return value as the tool result `content` field in session history. Anthropic-protocol providers reject non-string content with a 400 on the next request — once triggered, every subsequent request in the affected session 400s until the session JSON is hand-cleaned. Wrapped all four return paths (`memory_recall`, `memory_save`, `memory_search`, unknown-tool) in `json.dumps()` and tightened the return-type annotation `Any → str` on both the abstract base and the concrete class. Matches the contract that `src/mcp/standalone.ts` already honors. (#255, closes [#254](https://github.com/rohitg00/agentmemory/issues/254) — thanks @KyoMio for the Anthropic-protocol-specific repro)

- **Hermes plugin: `hermes memory status` now reflects the real service state on systemd / launchd installs.** When agentmemory runs as an external service whose runtime config lives in `~/.agentmemory/.env`, those values never reach the Hermes CLI shell. Hermes status reads `os.environ` against `get_config_schema()`'s `env_var` keys, finds them unset, and reports the plugin as "not available" — even though the service is healthy. The plugin now preloads `~/.agentmemory/.env` at import time using `os.environ.setdefault`, bridging the agentmemory-managed and Hermes-managed config source-of-truths. Anything explicitly exported in the shell still wins. Best-effort: malformed / absent file is silently skipped. Both `~/.agentmemory/.env` and `$XDG_CONFIG_HOME/agentmemory/.env` are checked. (#253, closes [#250](https://github.com/rohitg00/agentmemory/issues/250) — thanks @OptionalCoin for the systemd repro and tracing it to env-source divergence)

- **Hermes plugin: memory provider hooks accept passthrough kwargs.** Hermes calls memory provider hooks with extra context kwargs (e.g. `session_id`) at runtime that the existing strict signatures rejected with `sync_turn() got an unexpected keyword argument 'session_id'`. Hooks "succeeded" from Hermes's perspective but every conversation turn silently failed sync. Added `**kwargs: Any` to `sync_turn`, `on_session_end`, `on_pre_compress`, `on_memory_write`, `prefetch`, `queue_prefetch`, and `shutdown`. Where Hermes passes `session_id`, the patch prefers it over the cached `self._session_id` so multi-session gateway contexts route to the right session. Same change applied to the abstract `MemoryProvider` fallback for the import-error path. (#252, closes [#249](https://github.com/rohitg00/agentmemory/issues/249) — thanks @OptionalCoin for the precise log analysis)

- **`agentmemory demo` now actually seeds observations.** `seedDemoSession` posted to `/agentmemory/observe` without `project` and `cwd`, which the API requires as non-empty strings, so every observation 400'd and the demo silently reported "Seeded 0 observations across 3 sessions". Two-line fix: re-stage `project` + `cwd` into the observe payload alongside `sessionId`. The smart-search queries the demo prints will now return real hits. (#251, closes [#229](https://github.com/rohitg00/agentmemory/issues/229) — thanks @seishonagon for the precise root-cause analysis)

- **LLM compression / summarization timeouts increased.** Larger sessions were hitting the 120s consolidation timeout under heavier workloads, leaving partial state. Bumped per-step ceilings to give slow providers (esp. local models) room to finish. (#213 — thanks @xuli500177)

- **`pi` / OpenClaw / Hermes integration fixes.** Tested round-trip fixes across the three integration plugins to keep them aligned with the latest hooks contract. (#230 — thanks @deepmroot)

### Changed

- **`iii-engine` pinned to v0.11.2 across every install path.** v0.11.6 introduces a new architecture where workers run inside sandboxed microVMs registered via `iii worker add`. agentmemory still uses the older `iii-exec watch + node dist/index.mjs` worker model from `iii-config.yaml`, which doesn't pass the new engine's stricter trigger validation cleanly — the worker drops into an EPIPE reconnect loop and recall stops working. Pinning to v0.11.2 (the last engine that runs agentmemory's current architecture cleanly) until we refactor agentmemory to register itself via `iii worker add` and run inside the new sandbox model.
- `src/cli.ts` auto-installer downloads `github.com/iii-hq/iii/releases/download/iii/v0.11.2/iii-<arch>.tar.gz` directly. Per-arch coverage: darwin arm64/x64, linux x64/arm64/armv7, win32 x64/arm64.
- Docker fallback pulls `iiidev/iii:0.11.2` instead of `:latest`.
- `docker-compose.yml` uses `image: iiidev/iii:${AGENTMEMORY_III_VERSION:-0.11.2}` so the override env var actually takes effect for compose users.
- Install instructions and Windows guide updated to point at the v0.11.2 release page.
- **Escape hatch:** `AGENTMEMORY_III_VERSION=<version>` overrides the pin for users who've moved to the sandbox model manually.
- Windows ZIP path detection in `runUpgrade` so the auto-installer doesn't try to pipe a `.zip` through `tar -xz`. (#260)
- **Follow-up tracked separately:** refactor agentmemory to register as a sandboxed worker via `iii worker add` so the pin can be lifted.

- **README documents how to extend agentmemory with `iii worker add`.** New "Powered by iii" section maps each `iii worker add <name>` to a concrete agentmemory capability — multi-instance memory, scheduled consolidation, durable retries on embeddings, sandboxed code exec, SQL state, extra MCP host. Lists only workers actually published to [workers.iii.dev](https://workers.iii.dev) with direct links. (#242)

- **README iii Console section corrected.** The console ships with `iii` as a subcommand; there's no separate installer. Replaced the bogus `curl install.iii.dev/console/main/install.sh` line, simplified the launch command to `iii console --port 3114`, and added the missing console pages to the capability table (Workers, Queues, Config, Flow). Replaced the dashboard screenshot with the Workers page so users see real agentmemory instances connected. (#243)

### Notes

If you're upgrading from <0.9.5 and have an existing vector index on disk, the new dim-guard will refuse to load if your active embedding provider declares a different dimension than what's persisted. This is the intended safe default — set `AGENTMEMORY_DROP_STALE_INDEX=true` to discard and rebuild from live observations, or re-embed against the new provider before starting.

If you've been on `iii-engine` v0.11.6 and noticed search returning empty after save, install agentmemory 0.9.5 fresh (or run `npx @agentmemory/agentmemory upgrade`) to pull pinned engine v0.11.2. v0.11.6 brings a new sandbox-everything-via-`iii worker add` model that agentmemory hasn't been refactored for yet — that work is tracked as a follow-up; this release just keeps existing users unblocked.

[0.9.5]: https://github.com/rohitg00/agentmemory/compare/v0.9.4...v0.9.5

## [0.9.4] — 2026-04-29

Bug-fix patch. Fixes a silent gap where the knowledge graph never auto-populated despite `GRAPH_EXTRACTION_ENABLED=true`, and adds a doctor check that detects when Claude Code fails to load plugin hooks.
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ npm install && npm run build && npm start

This starts agentmemory with a local `iii-engine` if `iii` is already installed, or falls back to Docker Compose if Docker is available. REST, streams, and the viewer bind to `127.0.0.1` by default.

Install `iii-engine` manually. **agentmemory currently pins `iii-engine` to `v0.11.2`** — `v0.11.6` ships a regression where engine-internal cron/http triggers fail validation and the agentmemory worker drops into an EPIPE reconnect loop. Override with `AGENTMEMORY_III_VERSION=<version>` once compat is verified manually.
Install `iii-engine` manually. **agentmemory currently pins `iii-engine` to `v0.11.2`** — `v0.11.6` introduces a new sandbox-everything-via-`iii worker add` model that agentmemory hasn't been refactored for yet. Pin lifts once the refactor lands. Override with `AGENTMEMORY_III_VERSION=<version>` if you've migrated to the sandbox model manually.

- **macOS arm64:** `mkdir -p ~/.local/bin && curl -fsSL https://github.com/iii-hq/iii/releases/download/iii/v0.11.2/iii-aarch64-apple-darwin.tar.gz | tar -xz -C ~/.local/bin && chmod +x ~/.local/bin/iii`
- **macOS x64:** swap `aarch64-apple-darwin` for `x86_64-apple-darwin`
Expand All @@ -431,7 +431,8 @@ agentmemory runs on Windows 10/11, but the Node.js package alone isn't enough

```powershell
# 1. Open https://github.com/iii-hq/iii/releases/tag/iii%2Fv0.11.2 in your browser
# (we pin to v0.11.2 — v0.11.6 has a regression that breaks agentmemory)
# (we pin to v0.11.2 until agentmemory refactors for the new sandbox
# model that engine v0.11.6+ requires)
# 2. Download iii-x86_64-pc-windows-msvc.zip
# (or iii-aarch64-pc-windows-msvc.zip if you're on an ARM machine)
# 3. Extract iii.exe somewhere on PATH, or place it at:
Expand Down
11 changes: 6 additions & 5 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
services:
iii-engine:
# Pinned to v0.11.2. v0.11.6 has a regression where engine-internal
# cron/http trigger registrations fail validation, causing the
# agentmemory worker to drop into an EPIPE reconnect loop and BM25
# search to return empty after save. Bump only after compat is
# verified end-to-end against a fresh agentmemory install.
# Pinned to v0.11.2 — the last engine that runs agentmemory's current
# worker model cleanly. v0.11.6 introduces a new sandbox-everything-
# via-`iii worker add` model that agentmemory hasn't been refactored
# for yet; the architectural mismatch surfaces as EPIPE reconnect
# loops and empty search after save. Bump only after agentmemory is
# refactored to register as a sandboxed worker.
#
# Override per-shell or via .env file:
# AGENTMEMORY_III_VERSION=0.11.7 docker compose up
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@agentmemory/agentmemory",
"version": "0.9.4",
"version": "0.9.5",
"description": "Persistent memory for AI coding agents, powered by iii-engine's three primitives",
"type": "module",
"main": "dist/index.mjs",
Expand Down
2 changes: 1 addition & 1 deletion plugin/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "agentmemory",
"version": "0.9.4",
"version": "0.9.5",
"description": "Persistent memory for AI coding agents -- captures tool usage, compresses via LLM, injects context into future sessions. 12 hooks, 51 MCP tools, 4 skills, real-time viewer.",
"author": {
"name": "Rohit Ghumare",
Expand Down
8 changes: 4 additions & 4 deletions plugin/scripts/session-end.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ async function main() {
method: "POST",
headers: authHeaders(),
body: JSON.stringify({ sessionId }),
signal: AbortSignal.timeout(5e3)
signal: AbortSignal.timeout(3e4)
});
} catch {}
if (process.env["CONSOLIDATION_ENABLED"] === "true") {
Expand All @@ -37,7 +37,7 @@ async function main() {
method: "POST",
headers: authHeaders(),
body: JSON.stringify({ olderThanDays: 0 }),
signal: AbortSignal.timeout(15e3)
signal: AbortSignal.timeout(6e4)
});
} catch {}
try {
Expand All @@ -48,15 +48,15 @@ async function main() {
tier: "all",
force: true
}),
signal: AbortSignal.timeout(3e4)
signal: AbortSignal.timeout(12e4)
});
} catch {}
}
if (process.env["CLAUDE_MEMORY_BRIDGE"] === "true") try {
await fetch(`${REST_URL}/agentmemory/claude-bridge/sync`, {
method: "POST",
headers: authHeaders(),
signal: AbortSignal.timeout(5e3)
signal: AbortSignal.timeout(3e4)
});
} catch {}
}
Expand Down
2 changes: 1 addition & 1 deletion plugin/scripts/stop.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ async function main() {
method: "POST",
headers: authHeaders(),
body: JSON.stringify({ sessionId }),
signal: AbortSignal.timeout(3e4)
signal: AbortSignal.timeout(12e4)
});
} catch {}
}
Expand Down
21 changes: 13 additions & 8 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ const IS_VERBOSE = args.includes("--verbose") || args.includes("-v");

// Pinned iii-engine version. The unpinned `install.iii.dev/iii/main/install.sh`
// script tracks `latest`, which made every fresh agentmemory install pull
// engine 0.11.6 — and 0.11.6 has a regression where its internal cron/http
// trigger registrations fail validation, the worker drops into an EPIPE
// reconnect loop, and BM25 search returns empty after save (visible to users
// as "demo can't reach worker" and recall always-empty). Override env var
// AGENTMEMORY_III_VERSION lets early adopters move forward when a fixed
// engine ships, without us cutting another agentmemory release.
// engine 0.11.6 — and 0.11.6 introduces a new sandbox-everything-via-
// `iii worker add` worker model that agentmemory hasn't been refactored
// for yet (we still use the old `iii-exec watch` config-file model). The
// architectural mismatch surfaces as EPIPE reconnect loops and empty
// search results after save. Pin to v0.11.2 — the last engine that runs
// agentmemory's current worker model cleanly — until the refactor lands.
// Override env var AGENTMEMORY_III_VERSION lets users on the sandbox
// model already point at a newer engine without us cutting a release.
const IIPINNED_VERSION =
process.env["AGENTMEMORY_III_VERSION"] || "0.11.2";

Expand Down Expand Up @@ -370,8 +372,11 @@ function installInstructions(): string[] {
" npx @agentmemory/agentmemory mcp",
"",
"Docs: https://iii.dev/docs",
`Why pinned: agentmemory hits a regression in iii v0.11.6. Override with`,
`AGENTMEMORY_III_VERSION=<version> if you've verified compat manually.`,
`Why pinned: iii v0.11.6 introduces the new sandbox-everything model`,
`(\`iii worker add\` registration). agentmemory still uses the older`,
`iii-exec config-file worker model and needs a refactor before it`,
`runs cleanly under the new engine. Override with`,
`AGENTMEMORY_III_VERSION=<version> when you've migrated manually.`,
];
}

Expand Down
2 changes: 1 addition & 1 deletion src/functions/export-import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ export function registerExportImportFunction(sdk: ISdk, kv: StateKV): void {
const strategy = data.strategy || "merge";
const importData = data.exportData;

const supportedVersions = new Set(["0.3.0", "0.4.0", "0.5.0", "0.6.0", "0.6.1", "0.7.0", "0.7.2", "0.7.3", "0.7.4", "0.7.5", "0.7.6", "0.7.7", "0.7.9", "0.8.0", "0.8.1", "0.8.2", "0.8.3", "0.8.4", "0.8.5", "0.8.6", "0.8.7", "0.8.8", "0.8.9", "0.8.10", "0.8.11", "0.8.12", "0.8.13", "0.9.0", "0.9.1", "0.9.2", "0.9.3", "0.9.4"]);
const supportedVersions = new Set(["0.3.0", "0.4.0", "0.5.0", "0.6.0", "0.6.1", "0.7.0", "0.7.2", "0.7.3", "0.7.4", "0.7.5", "0.7.6", "0.7.7", "0.7.9", "0.8.0", "0.8.1", "0.8.2", "0.8.3", "0.8.4", "0.8.5", "0.8.6", "0.8.7", "0.8.8", "0.8.9", "0.8.10", "0.8.11", "0.8.12", "0.8.13", "0.9.0", "0.9.1", "0.9.2", "0.9.3", "0.9.4", "0.9.5"]);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
if (!supportedVersions.has(importData.version)) {
return {
success: false,
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ export interface ExportPagination {
}

export interface ExportData {
version: "0.3.0" | "0.4.0" | "0.5.0" | "0.6.0" | "0.6.1" | "0.7.0" | "0.7.2" | "0.7.3" | "0.7.4" | "0.7.5" | "0.7.6" | "0.7.9" | "0.8.0" | "0.8.1" | "0.8.2" | "0.8.3" | "0.8.4" | "0.8.5" | "0.8.6" | "0.8.7" | "0.8.8" | "0.8.9" | "0.8.10" | "0.8.11" | "0.8.12" | "0.8.13" | "0.9.0" | "0.9.1" | "0.9.2" | "0.9.3" | "0.9.4";
version: "0.3.0" | "0.4.0" | "0.5.0" | "0.6.0" | "0.6.1" | "0.7.0" | "0.7.2" | "0.7.3" | "0.7.4" | "0.7.5" | "0.7.6" | "0.7.7" | "0.7.9" | "0.8.0" | "0.8.1" | "0.8.2" | "0.8.3" | "0.8.4" | "0.8.5" | "0.8.6" | "0.8.7" | "0.8.8" | "0.8.9" | "0.8.10" | "0.8.11" | "0.8.12" | "0.8.13" | "0.9.0" | "0.9.1" | "0.9.2" | "0.9.3" | "0.9.4" | "0.9.5";
exportedAt: string;
sessions: Session[];
observations: Record<string, CompressedObservation[]>;
Expand Down
2 changes: 1 addition & 1 deletion src/version.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const VERSION = "0.9.4";
export const VERSION = "0.9.5";
2 changes: 1 addition & 1 deletion test/export-import.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ describe("Export/Import Functions", () => {
it("export produces valid ExportData structure", async () => {
const result = (await sdk.trigger("mem::export", {})) as ExportData;

expect(result.version).toBe("0.9.4");
expect(result.version).toBe("0.9.5");
expect(result.exportedAt).toBeDefined();
expect(result.sessions.length).toBe(1);
expect(result.sessions[0].id).toBe("ses_1");
Expand Down
Loading