68 Commits

Author SHA1 Message Date
Rohit Ghumare 9061da56d5 chore(release): v0.9.18 (#459)
Hardening + DX wave. Bundles the four follow-up issues filed during
the v0.9.17 audit (#446 timeout env unify, #447 viewer CSP tighten,
#448 fs-watcher PEM/JWT redaction, #449 mcp-standalone probe DI flake)
plus the lessons-auto-inject gap surfaced in discussion #381 / fixed
in #457.

1007/1007 tests pass.

Files bumped:
- package.json
- packages/mcp/package.json
- plugin/.claude-plugin/plugin.json
- plugin/.codex-plugin/plugin.json
- src/version.ts
- src/types.ts (ExportData.version union)
- src/functions/export-import.ts (supportedVersions Set)
- test/export-import.test.ts (assertion)
- CHANGELOG.md (new section)
2026-05-17 14:05:56 +01:00
Rohit Ghumare 3a3f866ab5 chore(release): v0.9.17 — OpenAI provider + telemetry id + Compare polish (#432)
Patch bump per the established rule: additive surface only. OpenAI
provider is a new optional surface that activates only when
OPENAI_API_KEY is set, gated by OPENAI_API_KEY_FOR_LLM. Telemetry
project_name pin is pure observability metadata. Compare polish is
docs/website only.

PRs included since v0.9.16:
  #307 — OpenAI-compatible LLM provider (universal adapter for
         OpenAI, Azure OpenAI auto-detected by hostname, DeepSeek,
         SiliconFlow, vLLM, LM Studio, Ollama). Plus the maintainer-
         pushed Azure detection + fetch timeout + README scope hint
         follow-ups. Closes #185, #232, #312, supersedes #240.
  #426 — pin worker telemetry project_name
  #427 — Compare section polish (title + native plugins cell + grid)

Files bumped (9):
  package.json, packages/mcp/package.json, plugin/.claude-plugin/plugin.json,
  plugin/.codex-plugin/plugin.json, src/version.ts, src/types.ts,
  src/functions/export-import.ts, test/export-import.test.ts,
  CHANGELOG.md
2026-05-16 20:36:24 +01:00
Rohit Ghumare b7e1240d06 chore(release): v0.9.16 — DevEx polish + website refresh (#417)
Patch bump per the established rule: additive surface only, no breaks
to MemoryProvider trait, exported types, or default behaviour. New
top-level subcommands (`--reset` already shipped 0.9.15, no new
commands here) are opt-in.

PRs included since v0.9.15:
  #408 — onboarding wires selected agents inline + memory-share callout
  #409 — clarify MCP is opt-in (REST primary)
  #410 — 5-port ready panel, iii console install, global-install prompt
  #411 — splash banner rerender + README install hoist + npx caveat
  #415 — agent-memory.dev refresh (FeaturedIn bar + Agents/Compare/
         CommandCenter/Hero updates)

Files bumped (9):
  package.json, packages/mcp/package.json, plugin/.claude-plugin/plugin.json,
  plugin/.codex-plugin/plugin.json, src/version.ts, src/types.ts,
  src/functions/export-import.ts, test/export-import.test.ts,
  CHANGELOG.md
2026-05-15 19:58:24 +01:00
Rohit Ghumare c3a613aed8 chore(release): v0.9.15 — DevEx overhaul (splash, connect, doctor v2, remove, silent killers) (#407)
Patch bump per the established rule: all changes additive, no breaks
to MemoryProvider trait or exported types or default behaviour. New
top-level subcommands (connect, remove, --reset, --force) are opt-in.

PRs included since v0.9.14:
  #405 — silent killers: viewer port auto-bump, engine version-match
         warning, stop --force, adopt-on-attach, npx PATH hint
  #402 — agentmemory connect — automate native-plugin install for 8
         agents (claude-code, codex, cursor, gemini-cli, openclaw
         end-to-end; hermes/pi/openhuman stubbed)
  #406 — interactive doctor v2 + agentmemory remove (destruction plan
         + two-confirmation flow)
  #403 — splash banner + agent grid + provider picker + smart-defaults
         preferences + bootLog shim (30+ lines of log spam → 10)

Files bumped (9):
  package.json, packages/mcp/package.json, plugin/.claude-plugin/plugin.json,
  plugin/.codex-plugin/plugin.json, src/version.ts, src/types.ts,
  src/functions/export-import.ts, test/export-import.test.ts,
  CHANGELOG.md
2026-05-15 16:43:06 +01:00
Rohit Ghumare 372c6a6bbb chore(release): v0.9.14 — CLI installer first + agentmemory stop + Docker-aware teardown (#401)
Patch bump. No breaking changes to the public API or the existing CLI
subcommands. CLI bootstrap flow changes are additive; existing Docker
users keep working via the new AGENTMEMORY_USE_DOCKER=1 opt-in.

Files bumped (9):
  - package.json
  - packages/mcp/package.json
  - plugin/.claude-plugin/plugin.json
  - plugin/.codex-plugin/plugin.json
  - src/version.ts
  - src/types.ts (ExportData.version union — also adds 0.9.13
    which was missed in the previous release)
  - src/functions/export-import.ts (supportedVersions Set)
  - test/export-import.test.ts
  - CHANGELOG.md

PRs included since v0.9.13:
  #396 — feat(cli): install iii-engine binary first, Docker opt-in;
         agentmemory stop with Docker-aware teardown; LISTEN-only
         lsof filter so the CLI no longer signals its own parent.
  #397 — docs(readme): move OpenHuman after pi in agents grid.
2026-05-15 15:11:47 +01:00
Rohit Ghumare 8b98432853 chore(release): v0.9.13 — env-example + init + CJK + load bench + deploy templates + Gemini GA bumps (#383)
* chore(release): v0.9.13 — env-example discovery + CJK tokenizer + load
harness + deploy templates + Gemini GA bumps + 14 advisories closed

Six PRs landed since v0.9.12:

- #372 .env.example discovery (this commit) — repo-root template + `init`
  CLI command + CI sync-checker
- #362 CJK BM25 tokenizer (`@node-rs/jieba` + tiny-segmenter + Hangul)
- #363 `benchmark/load-100k.ts` harness with p50/p90/p99 + per-release
  results dir
- #361 one-click deploy templates for fly.io / Railway / Render / Coolify
  (multi-stage Dockerfile, `iiidev/iii` base, `gosu` privilege drop,
  first-boot HMAC, verified end-to-end on fly.io)
- #364 Python ecosystem via `iii-sdk` example (replaces closed PR #360)
- #370 Gemini GA bumps (LLM default → gemini-2.5-flash, embedding →
  gemini-embedding-001 + L2-norm + 768 dims)

Plus 14 open Dependabot advisories closed in PR #348 via Next.js → 16.2.6
and PostCSS → 8.5.10 overrides.

Bumped:

- src/version.ts: VERSION 0.9.12 → 0.9.13
- package.json: 0.9.12 → 0.9.13, files += ".env.example", build script
  copies .env.example into dist/
- packages/mcp/package.json: 0.9.12 → 0.9.13 (lockstep with main)
- plugin/.claude-plugin/plugin.json, plugin/.codex-plugin/plugin.json:
  0.9.12 → 0.9.13
- src/types.ts: ExportData.version union extended with "0.9.13"
- src/functions/export-import.ts: supportedVersions Set extended
- test/export-import.test.ts: expected version updated

New surface:

- .env.example at repo root — every env var read by src/ documented in
  one place, grouped by surface (LLM, embedding, auth, search tuning,
  behaviour flags, CLI runtime, ports, iii engine pin, Claude Code
  bridge, Obsidian export). Every line commented out by default so the
  file is a template.
- agentmemory init — copies bundled .env.example to ~/.agentmemory/.env
  if absent, refuses to overwrite, prints a diff command. Wired into
  CLI dispatch + help block.
- scripts/check-env-example.mjs — walks src/ for env-read patterns,
  fails CI on drift in either direction. Plugged into ci.yml after
  npm test. Initial bootstrap: 60 keys in sync.

Verified: npm test 903/903, npm run build clean, init smoke pass
(creates ~/.agentmemory/.env on first run, refuses overwrite on second).

* fix(init): atomic copy via COPYFILE_EXCL; address CodeRabbit review

Two valid findings from the CodeRabbit pass on PR #383.

1. `runInit` race between existsSync(target) + copyFile(template, target).
   A parallel `agentmemory init` (or any other process touching
   ~/.agentmemory/.env between the two calls) would silently overwrite
   the config the operator just wrote. Switch to a single atomic
   `copyFile(template, target, fsConstants.COPYFILE_EXCL)` and treat
   the EEXIST error as the "already configured" signal — same warning
   + diff hint as before, but the check + copy now happen in one
   syscall so they cannot race. Other failure paths still surface as
   process exit 1.

2. Comment on `scripts/check-env-example.mjs::walk` claimed it matched
   ".ts / .mts / .mjs" but the regex also matched ".js". Rewrote the
   comment to match the regex (".ts / .mts / .mjs / .js"). Same comment
   pass: noted that test/ never enters because the walk is rooted at
   src/, not because of an explicit skip.

Skipped findings:

- WHAT-style comment on `findEnvExample` — kept a one-liner explaining
  the package-vs-source priority since both paths are real; reduced
  the block from 4 lines to 2 instead of removing it entirely.
- "Add trailing newline to .env.example" — file already ends with `\n`
  (verified `tail -c 5` shows `tion\n`).

Verified locally:
- `npm run build` clean.
- `npm test` 903 / 903 pass.
- First `agentmemory init` against a clean HOME creates the file.
- Second init against the same HOME hits EEXIST and prints the
  "leaving it untouched" warning + diff hint without overwriting.
- `node scripts/check-env-example.mjs` — in sync (60 keys).
2026-05-15 10:11:37 +01:00
Rohit Ghumare 87fae50b79 chore(release): v0.9.12 — BM25 unicode + vector live-write + viewer hardening (#337)
Three landed PRs since v0.9.11:
  - #327 (#295) — BM25 tokenizer now accepts non-ASCII (Greek,
    accented Latin, Hebrew, Arabic, Cyrillic), VectorIndex.add now
    actually called at runtime via vectorIndexAddGuarded helper with
    dim guard + input clip, migrateVectorIndex for dim migrations.
  - #326 (#277) — RetentionScore type no longer declares source
    twice; JSDoc back-compat note no longer shadowed.
  - #335 (#323) — viewer drops Google Fonts <link> (CSP-blocked),
    loadDashboard now surfaces load errors inline instead of
    sticking on "Loading dashboard…".

Bumping 0.9.11 -> 0.9.12 across the 9 standard files:
- package.json
- packages/mcp/package.json
- plugin/.claude-plugin/plugin.json
- plugin/.codex-plugin/plugin.json
- src/version.ts
- src/types.ts (ExportData.version literal)
- src/functions/export-import.ts (supportedVersions)
- test/export-import.test.ts (round-trip expectation)
- CHANGELOG.md (new 0.9.12 entry)

886 / 886 tests pass. Build clean.
2026-05-13 16:17:01 +01:00
Rohit Ghumare 25dddc4379 chore(release): v0.9.11 — Codex plugin platform + OpenClaw slot fix + website star button (#317)
Three landed PRs since v0.9.10:
- #311 — Codex plugin support (manifest + marketplace + Codex-shaped
  hooks subset, sharing the same scripts as the Claude Code plugin
  via CLAUDE_PLUGIN_ROOT OOTB-compat injection on Codex's hook
  engine)
- #310 — OpenClaw plugins.slots.memory now actually claimed via
  api.registerMemoryCapability({ promptBuilder }); older builds
  without the capability API still load via the existing hook-only
  path
- #316 — Star on GitHub button in the website hero CTA row, live
  stargazer count via api.github.com, 30-minute localStorage cache,
  graceful degrade on API failure

Bumping 0.9.10 -> 0.9.11 across the 8 standard files:
- package.json
- packages/mcp/package.json
- plugin/.claude-plugin/plugin.json
- plugin/.codex-plugin/plugin.json
- src/version.ts
- src/types.ts (ExportData.version literal)
- src/functions/export-import.ts (supportedVersions)
- test/export-import.test.ts (round-trip expectation)
- CHANGELOG.md (new 0.9.11 entry)

877 / 877 tests pass. Build clean.
2026-05-13 00:21:27 +01:00
Rohit Ghumare c21410e287 feat(plugin): ship Codex plugin manifest + marketplace (#311)
* feat(plugin): ship Codex plugin manifest + marketplace (#codex-plugins)

OpenAI Codex shipped a plugin platform
(developers.openai.com/codex/plugins) with the same shape as Anthropic
Claude Code plugins: .codex-plugin/plugin.json manifest, optional
.mcp.json, hooks/hooks.json, and a skills/ directory.

Verified against openai/codex source
(codex-rs/hooks/src/engine/discovery.rs): Codex's hook engine
explicitly injects CLAUDE_PLUGIN_ROOT into hook subprocesses for
OOTB compat with existing Claude Code plugins. The wire-format
input schemas
(codex-rs/hooks/schema/generated/session-start.command.input.schema.json
and siblings) use the same field names as Claude Code
(session_id, cwd, hook_event_name, source, transcript_path, model,
permission_mode).

Net result: our existing plugin/ directory already works as a Codex
plugin with two small additions:

1. plugin/.codex-plugin/plugin.json — Codex manifest pointing at the
   shared ./.mcp.json, ./skills/, and a Codex-specific ./hooks/
   hooks.codex.json. Name (kebab-case), version, description match
   the Claude Code manifest.

2. plugin/hooks/hooks.codex.json — Codex-compatible hook subset.
   Drops SubagentStart, SubagentStop, SessionEnd, Notification,
   TaskCompleted, PostToolUseFailure (Claude-Code-only). Keeps
   SessionStart, UserPromptSubmit, PreToolUse, PostToolUse,
   PreCompact, Stop. Adds Codex-specific `statusMessage` decorations
   on the two hooks the user sees most (SessionStart + UserPromptSubmit).
   The script commands themselves still reference ${CLAUDE_PLUGIN_ROOT}
   because Codex's engine injects it.

3. .codex-plugin/marketplace.json at repo root — git-subdir source
   pointing at ./plugin so `codex plugin marketplace add rohitg00/
   agentmemory` works the same way `claude plugin marketplace add`
   does. Mirrors the existing .claude-plugin/marketplace.json shape
   but in Codex's marketplace schema (name + interface.displayName
   + plugins[].source.{source, url, path, ref} + plugins[].policy).

README updates:
- Codex CLI tile in the Works-with-every-agent grid now reads
  "6 hooks + MCP + skills" instead of "MCP server" to surface the
  upgraded surface.
- "Other agents" install table now distinguishes "Codex CLI (MCP
  only)" (existing codex mcp add path) from "Codex CLI (full
  plugin)" (new marketplace install).
- New Codex section in the "paste this prompt" block with the
  two-command install (server + marketplace add + plugin install),
  the list of registered surfaces, and the env-var-injection note
  citing the Codex source line.

6 new tests in test/codex-plugin.test.ts cover:
1. .codex-plugin/plugin.json present with kebab-case name and
   required references.
2. Manifest version matches main package.json (so future bumps
   don't drift the Codex side).
3. Every path referenced in the manifest resolves to a real file or
   directory on disk.
4. hooks.codex.json contains only events Codex supports — any
   future addition has to be added to a Codex allowlist or the
   test fails loudly.
5. Every ${CLAUDE_PLUGIN_ROOT}/scripts/* command references an
   existing script file (catches typos before users hit them).
6. .codex-plugin/marketplace.json declares git-subdir source with
   path: "./plugin" pointing at this repo.

874 / 874 tests pass.

* fix(plugin/codex): align manifest description + assert PreCompact hook

Two reviewer findings addressed:

1. plugin/.codex-plugin/plugin.json's description claimed "12 hooks"
   but Codex only registers 6 (the Codex hook input schemas don't
   define SubagentStart, SubagentStop, SessionEnd, Notification,
   TaskCompleted, PostToolUseFailure). Updated the description to
   "6 hooks, 51 MCP tools, 4 skills, real-time viewer." so the
   marketplace listing matches the registered surface.

2. test/codex-plugin.test.ts asserted five lifecycle events
   (SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop)
   but omitted PreCompact, leaving a regression hole: dropping
   PreCompact from hooks.codex.json would still pass the test
   because the allowlist loop only blocks UNKNOWN events. Added
   expect(events).toContain("PreCompact") so the suite enforces
   the full six-hook contract.

Skipped (with reason):

3. marketplace.json source.ref currently "main" — reviewer asked
   for a fixed tag/SHA. Skipping because pinning to a tag forces
   updating marketplace.json on every release; "main" matches our
   convention where releases tag from main HEAD and pre-release
   work is on feature branches. Users wanting an immutable cut
   can pass --ref vX.Y.Z to `codex plugin marketplace add` per
   Codex docs.

Validation: 6/6 codex tests pass; 874/874 full suite unchanged.
2026-05-12 23:39:10 +01:00
Rohit Ghumare 5ea01c162c fix(deploy): distroless volume perms + viewer proxy + budget loop (#299, #301) (#304)
@flamerged reported three issues on a real production deployment:

1. (#301) v0.9.7's working-directory fix moved iii-config paths from
   ./data/... to /data/... so the named volume mount is actually
   reached. But iiidev/iii is distroless and runs as UID 65532, while
   `docker volume create` initializes the named volume mountpoint as
   root:root mode 755. Engine writes fail Permission denied (os error
   13), the API silently buffers in RAM, every API call returns
   success, and state evaporates on every container restart — exactly
   what 0.9.7 set out to fix.

   Fix: ship a one-shot iii-init service in docker-compose.yml
   (busybox:1.36, ~4MB, exits in <100ms) that chowns /data to
   65532:65532. iii-engine now has user: "65532:65532" and
   depends_on.iii-init.condition: service_completed_successfully.
   Verified live: pre-fix volume stayed 4.0K after API writes; post-
   fix volume grows to 44K with state_store.db/mem%3A*.bin files
   written through the named volume.

2. (#299) src/viewer/index.html ports detection hardcoded ':3113' as
   the fallback when window.location.port is empty (page served on
   80/443 behind a reverse proxy). Every browser-side /agentmemory/*
   fetch went to <host>:3113, which is typically loopback-only on the
   self-hosted shape — the dashboard rendered cleanly but every
   panel showed the empty "first run" state.

   Fix: when neither ?port=N nor window.location.port is set, use
   window.location.origin as the REST base and window.location.host
   for the WebSocket URL — same-origin path works for both REST and
   live updates. Explicit ?port=N / non-default window.location.port
   paths unchanged.

3. (bundled) mem::context budget loop used `break` on first oversized
   block. With #288's new pinned-slot injection sorting first via
   recency: Date.now(), one fat pinned slot could starve every
   smaller block downstream that would have fit. Switched to
   `continue` so smaller blocks still slip into remaining budget.
   Total tokens still bounded by tokenBudget; only composition under
   contention changes.

Bumping 0.9.9 -> 0.9.10 across the 8 standard files (package.json,
packages/mcp/package.json, plugin/.claude-plugin/plugin.json,
src/version.ts, src/types.ts ExportData literal,
src/functions/export-import.ts supportedVersions, the export
round-trip test expectation, and CHANGELOG.md).

868 / 868 tests pass. Build clean. Volume + viewer fixes verified
end-to-end live.
2026-05-12 16:19:13 +01:00
Rohit Ghumare 03fb42db39 chore(release): v0.9.9 — pinned slot injection + MiniMax env loader (#290)
Two field-reported fixes landed since v0.9.8:

- #288 (closes #286) — wires pinned memory slots into mem::context so
  agentmemory pinned-slot content actually reaches SessionStart. The
  renderPinnedContext / listPinnedSlots helpers from #182 had zero
  callers; this lands the wiring behind isSlotsEnabled().
- #289 (closes #285) — MiniMax provider now reads MINIMAX_BASE_URL via
  the shared getEnvVar() loader (so ~/.agentmemory/.env values get
  picked up), and the default endpoint is bumped from the stale
  api.minimaxi.com to api.minimax.io/anthropic per MiniMax's current
  Anthropic-compatible docs.

Bumping 0.9.8 -> 0.9.9 across the 8 standard files (package.json,
packages/mcp/package.json, plugin/.claude-plugin/plugin.json,
src/version.ts, src/types.ts ExportData literal,
src/functions/export-import.ts supportedVersions, the export
round-trip test expectation, and CHANGELOG.md).

868 / 868 tests pass.
2026-05-11 19:38:20 +01:00
Rohit Ghumare 0f0bb5091d chore(release): v0.9.8 — local fallback tools/list returns 7 not 4 (#284)
Single-issue release: PR #283 fixed the local-mode tools/list path
in @agentmemory/mcp to expose all 7 IMPLEMENTED_TOOLS instead of the
4-tool intersection ESSENTIAL_TOOLS ∩ IMPLEMENTED_TOOLS that bug
manifested as in MCP clients (Cursor / Roo Code / others) when no
agentmemory server was reachable on localhost:3111.

Bumping 0.9.7 -> 0.9.8 across the 8 standard files (package.json,
packages/mcp/package.json, plugin/.claude-plugin/plugin.json,
src/version.ts, src/types.ts ExportData literal,
src/functions/export-import.ts supportedVersions, the export
round-trip test expectation, and CHANGELOG.md).
2026-05-11 14:56:06 +01:00
Rohit Ghumare 82d1b06df5 fix(mcp): probe diagnostics + sandbox escape hatch (#234, #278) (#280)
* fix(mcp): probe diagnostics + sandbox escape hatch (#234, #278)

Three small follow-ups to v0.9.6 reported live on #234 and #278:

1. @agentmemory/mcp shim silently degraded to 7-tool local fallback for
   sandboxed MCP clients (Flatpak VS Codeium / Roo Code) because the
   500ms livez probe failed inside the sandbox network namespace and the
   catch swallowed the error. Probe now logs the URL, status, and reason
   to stderr; default timeout raised to 2000ms; AGENTMEMORY_PROBE_TIMEOUT_MS
   overrides the timeout; AGENTMEMORY_FORCE_PROXY=1 skips the probe and
   trusts AGENTMEMORY_URL outright for sandboxed clients that can reach
   the server through a known route but not the host loopback. Closes
   the #234 follow-up reported by @jcalfee.

2. Docker compose stack persisted state to an ephemeral container path.
   iii-config.docker.yaml used file_path: ./data/state_store.db, which
   the engine resolved against its container WORKDIR=/home/nonroot --
   not the /data mount where iii-data is bound. State and stream stores
   were silently written to the container layer and lost on every
   docker compose down. Paths are now absolute (/data/state_store.db
   and /data/stream_store). Existing users need a one-time docker
   compose down -v before upgrade.

3. CLI banner leaked "which: no iii in ..." when iii wasn't on PATH.
   execFileSync default stdio inherits stderr; GNU which writes the
   miss line to stderr (exit 1). Switched to explicit
   stdio: ["ignore", "pipe", "pipe"] in whichBinary().

4. docker-compose.yml now caps engine container log size at 30MB total
   (json-file driver, max-size=10m, max-file=3) so the iiidev/iii
   crash/restart spam reported by @satabd in #278 can no longer fill
   the host disk. The upstream engine spam itself needs filing against
   iii-hq/iii -- this is the compose-side guardrail.

Version 0.9.6 -> 0.9.7 across package.json, packages/mcp/package.json,
plugin/.claude-plugin/plugin.json, src/version.ts, src/types.ts
ExportData literal, src/functions/export-import.ts supportedVersions,
and the export round-trip test expectation.

* fix(mcp): AGENTMEMORY_DEBUG flag + loud unexpected-shape warning

@jcalfee on #234 confirmed the v0.9.6 fix (and PR #280's escape hatch)
still surfaces 7 tools in Roo Code even though:
  - Flatpak share=network is on (probe succeeds via curl in-sandbox)
  - server's /agentmemory/mcp/tools returns 51 tools via curl
  - AGENTMEMORY_FORCE_PROXY=1 is set

Roo Code shows 7 tools even when docker is down -- strong signal of
client-side caching at the Roo Code layer, not a shim regression.
But the shim has zero visibility into what it actually returns to
the MCP client right now: success path is silent, only failure paths
log.

Add AGENTMEMORY_DEBUG=1 (or =true) so the shim writes to stderr:
  - which mode handle.mode resolved to (proxy vs local) and baseUrl
  - shape of the /agentmemory/mcp/tools response (keys + tools type)
  - count of tools returned to the MCP client
  - tool names in the local-fallback list if we hit it

Also: previously, when the server returned a JSON shape that wasn't
{tools: Array}, the shim silently fell back to the 7-tool local list
with no log line. Now that path warns to stderr unconditionally,
pointing at AGENTMEMORY_DEBUG=1 for inspection.

This is diagnosis enablement -- not a fix for whatever is making
Roo Code show 7 tools. Once @jcalfee can run the shim with
AGENTMEMORY_DEBUG=1 and share the stderr output, we'll know whether
the shim returns 51 (Roo Code bug) or 7 (deeper shim bug).
2026-05-11 13:59:11 +01:00
Rohit Ghumare 13924d2f1b chore(release): v0.9.6 — search recall + MCP shim + hook latency (#272)
Three reliability fixes from #269/#270/#271:

- search/recall surfaces saved memories (closes #265)
- MCP shim proxies full server tool set (closes #234)
- session/subagent hooks no longer block startup (closes #221)

Also fixes packages/mcp version drift — was stuck at 0.9.4 through v0.9.5,
now lockstepped with main.
2026-05-10 19:25:41 +01:00
Rohit Ghumare 1ff5849d9c fix: cap session-start/subagent-start hook latency (#221) (#271)
Two hook scripts blocked Claude Code's startup waiting on REST responses
they didn't actually need:

- `session-start` awaited a 5000ms POST and discarded the response when
  `AGENTMEMORY_INJECT_CONTEXT=false` (the default). Pure latency.
- `subagent-start` had a `// fire and forget` comment but the code
  awaited a 2000ms POST. Pure latency.

Under fan-out (Slack-bot orchestrators, multi-agent harnesses, fanned
`claude -p` jobs) the awaited timeouts stack and feed back into the
engine; the reporter hit a positive feedback loop that OOM-killed
iii-engine.

Fix:

- `session-start` — fire-and-forget when `INJECT_CONTEXT=false`. Cap the
  inject path at 1500ms (down from 5000ms) so a slow server can't block
  the agent indefinitely when stdout is actually consumed.
- `subagent-start` — actually fire-and-forget, matching the existing
  comment. Cap at 800ms.

Verified live against a black-hole TCP listener (accepts, never replies):
- session-start (no inject): 5.05s → 0.85s
- session-start (inject):    5.05s → 1.55s
- subagent-start:            2.05s → 0.87s

Built artifacts in `plugin/scripts/` regenerated via `npx tsdown`.

Closes #221.
2026-05-10 19:08:34 +01:00
Rohit Ghumare 0c73d868be chore(release): v0.9.5 — search recall + plugin compatibility (#261)
Bug-fix patch focused on search recall correctness and plugin
compatibility. Pins iii-engine to v0.11.2 because v0.11.6 introduces
a new sandbox-everything-via-`iii worker add` model that agentmemory
hasn't been refactored for yet — pin lifts once that refactor lands.
Adds a hard guard against silent vector-index corruption, fixes BM25
indexing for memories saved via memory_save, and lands four Hermes
plugin fixes.

Per AGENTS.md release checklist:
- package.json version 0.9.4 -> 0.9.5
- src/version.ts VERSION constant
- src/types.ts ExportData version union
- src/functions/export-import.ts supportedVersions Set
- test/export-import.test.ts assertion
- plugin/.claude-plugin/plugin.json version
- CHANGELOG.md detailed entries with contributor shoutouts

Headlines (full detail in CHANGELOG):

Fixed:
- BM25 search now indexes memories saved via memory_save (#258, #257)
  Thanks @Nizar-BenHamida for the precise repro.
- Embedding providers no longer silently corrupt the vector index when
  an API returns wrong-dimension vectors (#248, #247, #256)
  Thanks @AmmarSaleh50 for issue + fix + tests.
- Hermes handle_tool_call returns JSON strings, not raw dicts (#255, #254)
  Thanks @KyoMio for the Anthropic-protocol repro.
- Hermes status reflects real service state on systemd installs (#253, #250)
  Thanks @OptionalCoin for tracing it to env-source divergence.
- Hermes hooks accept passthrough kwargs (#252, #249)
  Thanks @OptionalCoin again for the log analysis.
- agentmemory demo now seeds observations correctly (#251, #229)
  Thanks @seishonagon for root-cause analysis.
- LLM compression / summarization timeouts increased (#213)
  Thanks @xuli500177.
- Pi / OpenClaw / Hermes integration plugin fixes (#230)
  Thanks @deepmroot.

Changed:
- iii-engine pinned to v0.11.2 across every install path (#260).
  v0.11.6 introduces a new `iii worker add` sandbox model that
  agentmemory still pre-dates; pin lifts when we refactor agentmemory
  to register as a sandboxed worker. Override with
  AGENTMEMORY_III_VERSION=<version> for users who've migrated manually.
- README documents iii worker add extension surface (#242).
- README iii Console install/launch commands corrected (#243).

Validated: 852/852 tests pass, npm run build clean.
2026-05-09 17:39:20 +01:00
Rohit Ghumare 94fc1191ef chore: bump version to 0.9.4 + add CHANGELOG (#216) 2026-04-29 17:26:04 +01:00
Rohit Ghumare 4bd0ceb72e release: v0.9.3 — DX patch (feature-flag visibility + doctor command)
Every disabled feature flag is now visible everywhere: viewer banner, CLI
status, doctor output, and REST error responses. New `agentmemory doctor`
command runs 10 diagnostic checks in one shot, each failure includes a
concrete fix hint. Viewer gains first-run hero pointing at `demo` command
and a footer with preset issue-report link.

Bumps across 8 files per release checklist:
  package.json, plugin/.claude-plugin/plugin.json, src/version.ts,
  src/types.ts, src/functions/export-import.ts, test/export-import.test.ts,
  packages/mcp/package.json, CHANGELOG.md

See CHANGELOG for full details.
2026-04-24 19:53:07 +01:00
Rohit Ghumare 196cbd6343 chore: bump version to 0.9.2 + add CHANGELOG
Rolls up #186 (OPENAI_BASE_URL / OPENAI_EMBEDDING_MODEL), #187 (Stop-hook
recursion 5-layer defense + NoopProvider + AGENTMEMORY_ALLOW_AGENT_SDK opt-in),
#188 (viewer empty-tabs + import-jsonl synthetic compression + auto-derived
lessons/crystals + richer session detail + audit/replay/frontier shape fixes),
#189 (OPENAI_EMBEDDING_DIMENSIONS + model-dimensions table), and #190
(README/website docs refresh).

Bumps: package.json, plugin/.claude-plugin/plugin.json, src/version.ts,
src/types.ts ExportData.version union, src/functions/export-import.ts
supportedVersions, test/export-import.test.ts assertion, and
packages/mcp/package.json shim (was stuck at 0.9.0).
2026-04-22 16:38:37 +01:00
Rohit Ghumare 51bcb09104 address CodeRabbit review on #187 + fix CI
Findings verified against current code on this branch; all four valid.

1. config.ts loadFallbackConfig (L281) — user could set
   FALLBACK_PROVIDERS=agent-sdk and bypass the AGENTMEMORY_ALLOW_AGENT_SDK
   gate added to detectProvider. Filter it out at the fallback layer too,
   with the same warning pointing at the opt-in flag.

2. summarize.ts (L87-92) — the empty_provider_response branch returned
   without recording failure metrics or a diagnostic log, unlike the
   parse/validation paths. Record the same metricsStore failure event and
   log provider name, prompt size, system size, and observation count so
   empty responses are visible in telemetry.

3. providers/agent-sdk.ts (L14-45) — setting
   process.env.AGENTMEMORY_SDK_CHILD = '1' without restoring it caused
   every subsequent .query() in the same parent process to hit the
   short-circuit guard and return '' (classified as a SDK child it is
   not). Capture prev, set in try, restore in finally (delete if prev
   was undefined). Child processes spawned during the for-await loop
   still inherit the marker because env is inherited at spawn time; we
   only restore after the loop completes.

4. plugin/scripts/sdk-guard-DI1NUOS9.mjs — tsdown extracted the shared
   guard helper into a hashed chunk. Hash rotates on every rebuild and
   churns the diff. Stopped using the shared module from hooks entirely
   and inlined the 6-line guard function into each hook .ts file
   instead. sdk-guard.ts stays in the tree because the unit tests cover
   it directly. Deleted the tracked hashed .mjs and confirmed no new
   chunk is emitted.

Also applied the CI two-step install (npm install --package-lock-only
then npm ci) on this branch, matching #184. Without it, npm ci fails
because lockfiles are gitignored.

Tests: 74 files / 819 tests pass.
2026-04-22 11:44:05 +01:00
Rohit Ghumare 5e63846b29 fix(hooks): break Stop-hook infinite recursion via agent-sdk fallback
Reported: a user with no provider API key and AGENTMEMORY_AUTO_COMPRESS=false
(which they believed protected them) hit unbounded recursion — Stop hook
POSTs /agentmemory/summarize, handler calls provider.summarize(), agent-sdk
provider spawns @anthropic-ai/claude-agent-sdk query(), which creates a full
CC-style child session that reads ~/.claude/settings.json, registers the
same plugin hooks, and fires its own Stop -> another child -> loop. ~579
ghost 'entrypoint: sdk-ts' sessions accumulated in a few minutes, draining
Claude Pro tokens.

#149 only added a stderr warning. AGENTMEMORY_AUTO_COMPRESS gated /compress
but never /summarize, so users who followed the warning's implied guidance
still got hit. Fix the loop at every layer:

1. config.ts detectProvider
   - Treat empty-string provider keys (ANTHROPIC_API_KEY=) as unset; they
     previously passed the truthiness check identically to a real key.
   - Stop defaulting to agent-sdk. When no key is set, return a 'noop'
     provider config and warn. Agent-sdk fallback now requires an explicit
     AGENTMEMORY_ALLOW_AGENT_SDK=true opt-in with a loud second warning.

2. providers/noop.ts (new) + providers/index.ts
   - NoopProvider implements MemoryProvider and returns empty strings for
     compress and summarize so callers can detect .name === 'noop' and
     short-circuit without spawning anything.
   - Add ProviderType 'noop' and wire it through createBaseProvider.

3. providers/agent-sdk.ts
   - Before spawning query(), check process.env.AGENTMEMORY_SDK_CHILD === '1'
     and return '' instead of recursing. Set the env var to '1' before the
     spawn so any child process (including the Agent SDK session's hooks)
     inherits it.

4. hooks/sdk-guard.ts (new) + all 12 hook scripts
   - Shared isSdkChildContext(payload) checks both AGENTMEMORY_SDK_CHILD=1
     and payload.entrypoint === 'sdk-ts' (CC writes this into the stdin
     jsonl for SDK-spawned sessions). Every hook script now bails early
     when that returns true, so even if one guard layer fails the others
     break the loop.

5. functions/summarize.ts
   - Short-circuit with {success:false, error:'no_provider'} when
     provider.name === 'noop' — never reach .summarize().
   - Treat an empty provider response as empty_provider_response instead
     of trying to parse it.

Tests: 74 files / 819 tests pass (+7 new in stop-hook-recursion-guard.test.ts).
Defense in depth means any ONE of the five layers breaks the loop.
2026-04-22 10:57:57 +01:00
Rohit Ghumare c70b60211a feat(slots): add editable pinned memory slots + idle reflection + global scope
Introduces memory slots as a new first-class memory primitive, orthogonal
to observations / memories / graph. A slot is a labelled, size-limited,
agent-editable chunk of context. 8 defaults seed on first run
(persona, user_preferences, tool_guidelines, project_context, guidance,
pending_items, session_patterns, self_notes). Agent reads/writes via
MCP tools; pinned slots serialise cleanly into SessionStart context.

Functions (`src/functions/slots.ts`):
- mem::slot-list
- mem::slot-get
- mem::slot-create
- mem::slot-append   (refuses writes over sizeLimit)
- mem::slot-replace  (refuses content over sizeLimit)
- mem::slot-delete
- mem::slot-reflect  (used by idle reflection)

REST (`src/triggers/api.ts`):
- GET    /agentmemory/slots
- GET    /agentmemory/slot?label=...
- POST   /agentmemory/slot
- POST   /agentmemory/slot/append
- POST   /agentmemory/slot/replace
- DELETE /agentmemory/slot?label=...
- POST   /agentmemory/slot/reflect

MCP tools (`src/mcp/tools-registry.ts`, `src/mcp/server.ts`):
- memory_slot_list
- memory_slot_get
- memory_slot_create
- memory_slot_append
- memory_slot_replace
- memory_slot_delete

event::session::stopped fires mem::slot-reflect via triggerVoid when
AGENTMEMORY_REFLECT=true. The reflector reads the last N observations,
extracts TODO-flavoured titles into pending_items, counts error /
command types into session_patterns, and records touched files in
project_context. Fire-and-forget: does not block summarise.

Slots carry a scope field ("project" | "global"). Global slots live at
KV.globalSlots ("mem:slots:global") — one brain across all projects for
things like persona / user_preferences / tool_guidelines. Project-
scoped slot with the same label shadows the global one on read.

- AGENTMEMORY_SLOTS=true    — enable the whole primitive
- AGENTMEMORY_REFLECT=true  — enable Stop-hook reflection (requires SLOTS)

Both default OFF. Matches the opt-in convention of auto-compress (#138),
inject-context (#143), vision embeddings (#179).

44 MCP tools → 50 across README (stats badge, install instructions,
config block) and plugin/.claude-plugin/plugin.json description. New
AGENTMEMORY_SLOTS and AGENTMEMORY_REFLECT entries in the config block.

test/slots.test.ts — 12 new cases: default seeding by scope, label
validation, create/get/append/replace/delete round-trip, duplicate-
create rejection, sizeLimit enforcement on append + replace, project
shadows global, listPinnedSlots + renderPinnedContext, reflect no-op
on empty session, reflect updates pending_items / session_patterns /
project_context from TODO + error + file signals.

Full suite: 800/800 pass (+12 new). Build clean.
2026-04-21 18:57:17 +01:00
Rohit Ghumare 241be2669c fix(vision): address CodeRabbit review on #179
- src/functions/image-refs.ts: delete imageEmbeddings BEFORE imageRefs
  and drop the silent .catch() so transient KV errors surface. Stops
  orphan searchable embeddings when the ref delete fails halfway.
- src/functions/vision-search.ts: record an audit row for every
  successful mem::vision-embed write (new "vision_embed" op). State-
  changing functions must leave an audit trail per the repo policy.
- src/functions/vision-search.ts: mem::vision-search now wraps raw
  queryImageBase64 into a data: URL before handing to embedImage, so
  the provider contract stays "data: URL or filesystem path".
- src/providers/embedding/clip.ts: drop the raw-base64 heuristic in
  loadImage. The old regex whitelisted "/" in the char class but the
  !src.includes("/") guard rejected any realistic base64 blob,
  falling through to readFile and throwing ENOENT. Now: data: URL or
  filesystem path only; callers with raw base64 pass a data: URL.
  Also drop unused RawImage.read type and collapse the redundant
  Float32Array copy in embedImage.
- src/triggers/api.ts: both handlers now whitelist fields with
  asNonEmptyString and parseOptionalPositiveInt before constructing
  the payload. Matches api::observe / api::search style, stops
  forwarding the raw req.body, clamps topK at the boundary.
- src/types.ts: add "vision_embed" to AuditEntry.operation union and
  rename EmbeddingProvider.embedImage's param to src (matches the
  implementation now that raw base64 isn't accepted).
- README.md, plugin/.claude-plugin/plugin.json: document
  AGENTMEMORY_IMAGE_EMBEDDINGS and bump tool count string.

Full suite still 794/794.
2026-04-21 12:07:47 +01:00
Rohit Ghumare 2edc02feb5 fix: resolve conflicts with main, address qodo-ai security findings
Resolves conflicts in:
- src/state/schema.ts (merged imageRefs + accessLog)
- src/functions/auto-forget.ts (kept audit, reordered ref decrement)
- src/functions/compress.ts (collapsed iii-sdk imports, s/ctx.logger/logger/)
- src/functions/evict.ts (kept audit + deleteAccessLog, deferred ref
  decrement until after delete succeeds)
- src/functions/retention.ts (kept source-aware delete routing, fetch mem
  from resolved scope, defer ref decrement)
- src/triggers/api.ts (kept main's registerFunction/trigger shape)
- src/types.ts (kept superset of AuditEntry.operation union)

qodo-ai action-required findings:
- mem::forget now decrements image refs for every deleted memory and
  observation (including the session-wipe branch), which stops sensitive
  screenshots leaking to ~/.agentmemory/images/ after user-initiated
  forget.
- Eviction / auto-forget now decrement refs only after the KV delete
  actually succeeds. The old order (decrement-then-delete) could
  desync ref counts when the delete threw.

Follow-on fixes:
- buildSyntheticCompression now carries modality and imageData through
  from raw to compressed, so the default zero-LLM path doesn't drop
  image metadata the viewer and governance paths rely on.
- Test harness updated for the current registerFunction (name, cb)
  signature and iii-sdk partial-mock pattern — 11/11 multimodal
  tests pass, full suite 788/788.

Closes #64.
2026-04-21 10:37:24 +01:00
Rohit Ghumare 21a34aa28c chore: bump version to 0.9.1 + add CHANGELOG 2026-04-21 00:30:27 +01:00
Rohit Ghumare 3862c3f0d4 chore: release v0.9.0
Bump version + ship CHANGELOG covering everything that merged since
v0.8.13:

- #118 security advisory drafts for v0.8.2 CVEs
- #132 semantic eviction routing + batched retention audit
- #157 iii console docs + vendored screenshots in README
- #160 (#158) health gated on RSS floor
- #161 (#159) standalone MCP proxies to the running server
- #162 (#125) mem::forget audit coverage + policy doc
- #163 (#62) @agentmemory/fs-watcher filesystem connector
- #164 Next.js website (website/ root, ship to Vercel)

Version bumps (8 files):
- package.json / package-lock.json (top + packages[''])
- plugin/.claude-plugin/plugin.json
- packages/mcp/package.json (self + ~0.9.0 dep pin)
- src/version.ts (union extended, assigned 0.9.0)
- src/types.ts (ExportData.version union)
- src/functions/export-import.ts (supportedVersions set)
- test/export-import.test.ts (export assertion)

Tests: 777 passing. Build clean.
2026-04-18 20:00:26 +01:00
Rohit Ghumare 11bef18927 feat: session replay tab + JSONL transcript import
Adds a Replay tab to the viewer that plays any stored session as a
scrubbable timeline (prompts, tool calls, tool results, responses)
with play/pause, speed control, and keyboard navigation.

Adds `agentmemory import-jsonl [path]` CLI subcommand and a matching
POST /agentmemory/replay/import-jsonl endpoint to backfill sessions
from Claude Code JSONL transcripts (default ~/.claude/projects).

All new logic routes through iii-sdk primitives: registerFunction for
mem::replay::load / mem::replay::sessions / mem::replay::import-jsonl,
and the existing HTTP trigger in api.ts for REST exposure.

Security: JSONL import rejects symlinks, paths containing sensitive
terms, and tolerates malformed lines without aborting the batch.
All imports are recorded in the audit log.
2026-04-17 00:26:14 +01:00
Rohit Ghumare 1bdeaade8a chore: release v0.8.12
Bump package and plugin versions, update export/import compatibility, and add changelog notes for token-efficient recall and memory_compress_file.
2026-04-16 10:39:40 +01:00
Rohit Ghumare 14da6c816c feat: add token-efficient recall and file compression tool (#150)
* feat: add token-efficient recall and file compression tool

Add memory_recall format and token budget support, plus a new memory_compress_file tool with API/MCP wiring, tests, and updated tool/endpoint docs.

* chore: remove temporary architecture assets

Delete temporary architecture and memory-cycle asset files added during implementation.
2026-04-16 10:36:17 +01:00
Rohit Ghumare 0023af2c53 fix: iii-sdk v0.11 getContext crash — add logger shim, v0.8.11 (#146)
* fix: replace getContext() with logger shim for iii-sdk v0.11 compatibility

iii-sdk v0.11 dropped getContext() entirely. 32 src/functions/*.ts files still
imported and called it, crashing node dist/index.mjs on first import with:
  SyntaxError: module 'iii-sdk' does not provide an export named 'getContext'

- Add src/logger.ts: thin stderr shim (info/warn/error) with identical call signature
- Remove getContext import and const ctx = getContext() from all 32 function files
- Replace ctx.logger.* with logger.* throughout
- Fix search.ts registerFunction({ id: ... }) -> registerFunction('id') for v0.11 API
- Update 45 test mock blocks from vi.mock("iii-sdk") to vi.mock("../src/logger.js")
- Bump to v0.8.11

Tests: 731/731 pass. Binary starts cleanly. Closes #116.

* fix: bump version to 0.8.11 in plugin.json, export-import supported versions, and test
2026-04-15 17:24:44 +01:00
Rohit Ghumare 5d70ecfb3c feat: migrate agentmemory to iii v0.11 and add upgrade command (#116)
* feat: migrate agentmemory to iii v0.11 and add upgrade command

Migrate the codebase from legacy iii-sdk v0.3 APIs to v0.11 trigger/register patterns, update runtime configs, and align stream/state behavior with newer engine semantics. Add a new `agentmemory upgrade` CLI command so users can refresh dependencies and iii runtime components with one entrypoint.

* chore: remove pnpm lockfile from migration PR

Drop pnpm-lock.yaml from this branch to keep the migration PR focused on source and config changes only.

* fix(ci): sync npm lockfile with iii-sdk v0.11 dependency

Update package-lock.json so npm ci in CI matches package.json after the iii-sdk migration.

* fix(ci): resolve build parse errors after v0.11 migration

Fix malformed braces introduced during API migration in triggers/health/branch-aware files so tsdown build passes in CI.

* fix: harden migration follow-up fixes and audits

Apply the reviewed v0.11 follow-up fixes across runtime, docs, and tests by tightening validation, correcting upgrade/error handling, and adding missing audit coverage on state mutations. This also addresses stream fallback behavior, lock consistency, retention source-bucket cleanup, and test/mock alignment so build and test remain green.

* fix: address 9 unresolved CodeRabbit findings on iii v0.11 migration

CodeRabbit flagged 9 remaining issues on #116 — all real. Each was
verified against the current branch code before applying. Two
findings were deliberately skipped as policy/scope issues and are
documented at the bottom.

### Applied (9 real findings)

- src/triggers/api.ts — api.ts numeric query param validation:
  multiple sites forwarded `parseInt(params.limit)` result to the
  downstream function without a Number.isFinite check. A non-numeric
  query value produced NaN at the iii-sdk trigger boundary. Added
  parseOptionalInt and parseOptionalFloat helpers at the top of the
  file, wired into api::crystal-list, api::lesson-list, and
  api::insight-list (5 sites total).

- src/triggers/api.ts — api::observe, api::context, api::session::start,
  api::session::end were forwarding req.body verbatim to sdk.trigger
  with no validation. Added explicit type checks mirroring the existing
  api::search pattern, construct a sanitized payload object before
  triggering.

- src/cli.ts — p.confirm() can return a cancel Symbol on Ctrl+C, which
  is truthy, so the upgrade path ran even when the user cancelled.
  Added p.isCancel() check and explicit boolean comparison.

- src/cli.ts — removed dead `failed` flag in runUpgrade: every caller
  already calls process.exit(1) immediately, so the final `if (failed)`
  branch was unreachable. Simplified requireSuccess accordingly.

- src/functions/leases.ts — mem::lease-renew recorded audit events as
  "lease_acquire", which conflated renewals with first claims. Renamed
  the audit event to "lease_renew" so downstream audit filters can tell
  the two operations apart.

- src/functions/relations.ts — the pair lock
  `mem:${firstId}:${secondId}` serialized concurrent relate(A,B) calls
  with identical pairs, but did NOT protect concurrent relate(A,B) +
  relate(A,C). Both modify memory A's `relatedIds` array, which is a
  classic last-writer-wins race producing lost relation edges. Fixed
  by replacing the pair lock with nested per-entity locks in canonical
  sort order: withKeyedLock(mem:firstId) wrapping withKeyedLock(mem:secondId).
  Since the ids are sorted deterministically, no deadlock is possible.

- src/functions/sketches.ts + src/functions/summarize.ts + new
  src/functions/audit.ts safeAudit helper — recordAudit was awaited
  after kv.set calls. An audit write failure would reject and the
  caller would see an error even though the target state was already
  persisted. New safeAudit wrapper swallows audit errors and logs them
  via ctx.logger.warn, preserving the mutation's success. Applied to
  all 11 audit sites in sketches.ts and the single site in summarize.ts.

- src/mcp/server.ts — three issues in the MCP handler:
  1. memory_profile's refresh flag used `args.refresh === "true"`,
     ignoring boolean `true` from MCP clients that send proper types.
     Now accepts both.
  2. memory_sketch_create built sketchPayload with
     `asNonEmptyString(args.title)` which could return undefined,
     then forwarded that to the downstream function. Added explicit
     validation + 400 response at the MCP boundary.
  3. memory_recall, memory_team_feed, memory_audit_query used
     `(args.limit as number) || 10` which replaced an explicit 0 with
     10. Changed to typeof check so explicit 0 (rare but legal) is
     preserved.

- src/functions/governance.ts — mem::governance-bulk used Promise.all
  for the delete batch, which fails fast on the first error. The audit
  record still said `deleted: candidates.length` even if only half
  actually succeeded. Switched to Promise.allSettled, split results
  into successfulIds and failures arrays, record audit with both counts
  plus the per-failure details for traceability.

- src/functions/mesh.ts — mem::mesh-register, mem::mesh-sync,
  mem::mesh-receive, mem::mesh-remove all dereferenced `data.*` without
  checking that data was passed. A TypeError would bubble up on null
  payload. Added early null/type guards returning structured errors.

- src/functions/obsidian-export.ts — resolveVaultDir(data.vaultDir)
  was called without validating that vaultDir was a string, and
  `new Set(data.types)` without validating types was an array of
  strings. Added explicit validation returning 400-style error
  responses.

- src/functions/retention.ts — the eviction loop silently swallowed
  kv.delete errors via a bare `continue`. Added ctx.logger.warn on
  catch, included memoryId and sourceBucket in the log, and now
  returns `failed` count alongside `evicted` in the response.

- src/viewer/index.html — two WebSocket bugs:
  1. connectWs assigned to the mutable global state.ws before binding
     handlers, so old sockets could have their callbacks fire and
     mutate retry/direct state after state.ws was overwritten by a
     new socket. Fixed by creating a local `ws` variable, binding
     all handlers to it, only then assigning state.ws = ws. Each
     handler guards `if (state.ws !== ws) return` so stale callbacks
     are dropped.
  2. handleStreamEvent routed EVERY incoming event to routeWsMessage,
     so non-observation events (session.activity, etc.) were being
     treated as timeline observations and causing UI confusion. Added
     a looksLikeObservation helper + event_type gate that only routes
     real observation payloads.

- test/retention.test.ts — added an explicit sourceBucket eviction
  test that seeds a semantic memory at high age, runs retention-score,
  then runs retention-evict at high threshold and asserts BOTH
  KV.semantic and KV.memories are empty. Proves the candidate.sourceBucket
  branch added in this PR actually routes to the right bucket.

- src/types.ts — side fix: the ExportData.version union on line 254
  used comma separators instead of pipes in 3 positions (0.7.9,
  0.8.0, 0.8.1 → needed | between them). tsdown strips types so the
  build passed, but tsc --noEmit would have thrown and any IDE showed
  squiggles. Pre-existing latent bug, fixed while in the file.

### Deliberately skipped

- retention.ts eviction audit (CodeRabbit asked to add recordAudit
  to the eviction loop): policy change, not a bug fix. Verified:
  auto-forget.ts, evict.ts, retention-evict, remember-forget all
  skip audit by convention — only governance audits. Adding audit
  everywhere is a policy decision tracked in issue #125.

- file-index.ts sequential → parallel session lookup: micro-opt,
  the loop is already cache-backed, negligible savings, not worth
  the readability cost.

Tests: 655/655. Build clean.

* fix(api): harden request validation at HTTP boundaries

Validate and sanitize session, observe, and context inputs plus numeric query params before forwarding to memory functions, returning 400 for invalid values instead of propagating malformed payloads. Also remove duplicate CLI command registration introduced during merge resolution.

* fix: address 6 new CodeRabbit findings on iii v0.11 migration (#116 round 2)

CodeRabbit's second review pass on #116 flagged 6 real issues introduced
by the previous round of fixes (commit 3e3ac7e). Each was verified
against the current code before applying.

### Fixed

- src/functions/governance.ts — mem::governance-bulk switched to
  Promise.allSettled in the previous round but fanned out every
  kv.delete concurrently. The governance endpoint can target tens of
  thousands of memories in a single call; firing them all at once
  overwhelms the state worker. Batched with BATCH_SIZE=50 chunks,
  awaited serially while preserving the per-batch allSettled so a
  single failure doesn't abort the rest.
- src/functions/governance.ts — the recordAudit call AFTER the bulk
  delete is already committed was still using `await recordAudit`, so
  an audit write failure would bubble up as a "failed" response even
  though the rows were actually deleted. Switched to safeAudit so the
  deletes are reflected faithfully in the response regardless of
  audit health.
- src/functions/relations.ts — the mem::relate handler was emitting
  three recordAudit calls INTERLEAVED with three kv.set writes:
  relation → audit → source update → audit → target update → audit.
  A thrown audit in the middle would leave KV.relations and
  source/target.relatedIds in a partial state. Restructured to
  complete all three durable writes first, then emit a single
  safeAudit at the end. Also changed the operation from the generic
  "evolve" (which made audit queries indistinguishable from actual
  version evolutions) to a new "relation_create" event, added to the
  AuditEntry.operation union in src/types.ts.
- src/functions/retention.ts — mem::retention-score used
  `{ ...DEFAULT_DECAY, ...data.config }` which is only a shallow
  merge. A caller passing `{ config: { tierThresholds: { cold: 0.2 } } }`
  would drop the hot and warm thresholds, causing every downstream
  `s.score >= config.tierThresholds.hot` comparison to produce NaN
  and misclassify every tier bucket. Deep-merged tierThresholds
  explicitly.
- src/functions/retention.ts — mem::retention-evict previously
  used `candidate.sourceBucket || KV.memories` as the delete bucket.
  That's correct for new retention scores (which now store
  sourceBucket) but wrong for pre-migration scores whose sourceBucket
  is undefined: the fallback would try to delete semantic ids from
  KV.memories and silently no-op, leaving the real semantic memory
  alive. Fixed by splitting the delete path: if sourceBucket is set,
  use it; otherwise attempt both KV.memories and KV.semantic with
  individual .catch(() => {}) wrappers so whichever bucket actually
  contains the id succeeds and the other is a no-op. Legacy rows
  retire naturally on the next scoring run, which writes sourceBucket.
- src/triggers/api.ts — api::obsidian-export was validated at the
  downstream mem::obsidian-export layer in the previous round, but
  the API boundary handler was still forwarding body.vaultDir without
  a type check. Added the same 400 response pattern used by
  api::search / api::observe / api::context for consistency.

### Skipped from CodeRabbit's suggestions

- CodeRabbit's proposed fix for retention.ts:249 was
  `candidate.sourceBucket || KV.retentionScores` which would try to
  delete the memory from the retention scores namespace — that's
  where the scoring entry lives, not where the memory lives. That
  fix is wrong. The two-bucket fallback approach above is the
  correct one.

Tests: 655/655. Build clean.

* fix(agentmemory): harden deletion, relation, and API validation paths

Bound retention/governance side effects and make audit logging best-effort so successful state writes are not masked by telemetry failures. Also tighten boundary validation for MCP/API inputs, resolve relation lock edge cases, and align retention test mocks with v0.11 trigger/registerFunction call shapes.

* fix: address remaining CodeRabbit validation and eviction issues

Remove duplicate CLI help examples, harden eviction/delete bookkeeping, allow file-context without sessionId, sanitize governance failure output, validate retention-evict inputs with bounded limits, and tighten summarize/observations/obsidian-export input validation.

* fix(v0.11): align stream send payloads and migration docs

Use the engine-compatible stream payload key `type` for stream::send events and update docs/plugin examples to reflect v0.11 function registration and trigger request shapes.
2026-04-15 14:02:22 +01:00
Rohit Ghumare 3bad5430ed fix: SessionStart context gate (#143) + retention-evict semantic leak (#124) (#145)
* fix: stop burning Claude Pro tokens on every tool call (#143)

0.8.8 fixed the agentmemory-side Claude API burn (where the engine
called Claude via the user's ANTHROPIC_API_KEY for per-observation
compression). That addressed #138 for users with API keys, but it
missed the second and much larger token-burn path: the PreToolUse
hook writing context to stdout.

Claude Code reads PreToolUse stdout and prepends it to the model's
next turn. src/hooks/pre-tool-use.ts was POSTing /agentmemory/enrich
on every Edit/Write/Read/Glob/Grep tool call and piping up to 4000
chars of response context into stdout. At ~20 tool calls per user
message this silently injected ~20K tokens per message into Claude
Code's input window — all charged against the user's Claude Pro
allocation because Claude Code was the one sending them to Anthropic.
4 messages drained the cap, which matches @adrianricardo's report.

session-start.ts had the same pattern (injected once per session,
smaller blast radius).

Fix: gate both hooks on AGENTMEMORY_INJECT_CONTEXT, default false.

- pre-tool-use.ts: when disabled, exit immediately — no stdin read,
  no fetch, no stdout write. The hot path (~20x per message) becomes
  a no-op Node startup.
- session-start.ts: when disabled, still POST /agentmemory/session/start
  so the session gets registered for observation tracking, but never
  write context to stdout. The session registration is cheap and
  doesn't touch Claude Code's input window.
- src/config.ts: new isContextInjectionEnabled() helper.
- src/index.ts: startup banner prints 'Context injection: OFF
  (default, #143)' on normal startup and a loud WARNING when
  opt-in is enabled.
- test/context-injection.test.ts: 5 subprocess tests that spawn the
  compiled pre-tool-use.mjs and session-start.mjs hooks, feed real
  JSON payloads via stdin, and assert stdout is empty in all the
  off/default paths. Also asserts the disabled path exits under 1s
  and the opt-in path with an unreachable backend still exits
  cleanly.
- README .env section: new AGENTMEMORY_INJECT_CONTEXT entry.
- CHANGELOG [0.8.10] with prominent 'Behavior change' banner.

Observations are still captured via PostToolUse regardless of the
flag — the memory store and MCP search tools are completely
unaffected by this change. The fix only severs the path where
agentmemory silently shoves memory context into the user's Claude
Code conversation.

Bumps to 0.8.10 (main + @agentmemory/mcp shim). Test count:
724 passing (was 719 + 5 new).

* chore: rewrite #143 CHANGELOG entry with corrected diagnosis

PreToolUse stdout is NOT injected into the model context — per
the Claude Code hook docs, only UserPromptSubmit and SessionStart
stdout are injected. My initial #143 PR description and CHANGELOG
claimed PreToolUse was the smoking gun behind 'Pro allocation
burned in 4 messages', which is wrong.

What's actually true:
- SessionStart stdout injection IS real (~1-2K tokens per session)
- PreToolUse stdout goes to debug log only — no tokens
- Claude Pro's Claude Code quotas are tight by design (Anthropic
  has publicly acknowledged this); 4 messages to burn is plausible
  with or without agentmemory installed

The gate on pre-tool-use.ts is still worth keeping as a resource
cleanup (skips a 20x-per-message Node+HTTP hot path) and as
forward-compat protection in case Claude Code ever changes
PreToolUse hook contract. But the CHANGELOG entry has to stop
claiming it saves tokens when it doesn't.

* fix: mem::retention-evict no longer leaks semantic memories (#124)

The eviction loop was unconditionally calling kv.delete(KV.memories,
id) for every below-threshold candidate, but retention scores are
computed for both episodic (KV.memories) and semantic (KV.semantic)
memories. When a candidate came from KV.semantic, the delete silently
became a no-op (key wasn't in mem:memories to begin with) and the
semantic row stayed alive forever with a sub-threshold score. Semantic
memories could not be evicted by this path at all.

Fix:
- Add a source: "episodic" | "semantic" discriminator to RetentionScore
- Tag it at score creation in both loops of mem::retention-score
- Branch the delete in mem::retention-evict on candidate.source,
  routing to KV.memories or KV.semantic accordingly
- Pre-0.8.10 retention rows with no source field are treated as
  episodic for backwards-compat so upgraded stores continue to evict
  their old rows without re-scoring first
- Response now includes evictedEpisodic and evictedSemantic counts
  so callers can see what was removed from each scope

Adds 3 regression tests to test/retention.test.ts:
- Scoring tags rows with the correct source
- Evicting a mixed set of below-threshold episodic + semantic
  candidates removes both from their respective scopes
- Legacy-shape score rows with no source field still evict
  to mem:memories (backwards-compat)

Full suite: 727 passing (was 724 + 3 new).

* review: probe namespaces for legacy retention rows (#124, round 2)

CodeRabbit caught a real backwards-compat hole in the #124 fix:
pre-0.8.10 stores already contain semantic retention rows with
no source field (because the old mem::retention-score scored
KV.semantic before the discriminator existed). My first fix
defaulted missing source to episodic, which meant those legacy
semantic rows still got delete-routed to KV.memories — the exact
no-op that stranded them in the first place.

Fix: when candidate.source is undefined, probe KV.memories first
for the memoryId; if it's there, route to episodic, otherwise
route to semantic. Count the resolved source in the response.

Adds one new test case: a pre-0.8.10 semantic memory with a
legacy-shape retention row (no source field) gets evicted from
mem:semantic, not silently no-op'd. Existing 'defaults to
episodic' test is kept and retargeted to the genuinely-episodic
legacy case.

Also fixes a README nit: the AGENTMEMORY_INJECT_CONTEXT comment
previously implied SessionStart fires on every tool turn. It's
once per session. Now broken out into two bullets explaining
what each hook does differently, with the note that only
SessionStart actually reaches the model (PreToolUse stdout is
debug-log only per Claude Code docs).

Full suite: 728 passing (was 727 + 1 new).

* review: audit retention evictions + assert persisted source (#124 round 3)

CodeRabbit round 3 findings, both real:

1. retention-evict performs structural deletes (memories / semantic /
   retention scores / access logs) but was not calling recordAudit().
   Repo learnings say state-changing functions must be auditable except
   for read-path bookkeeping. Now emits one batched audit row per
   non-zero eviction sweep:

     operation: 'delete'
     functionId: 'mem::retention-evict'
     targetIds: every evicted memoryId
     details: { threshold, evicted, evictedEpisodic, evictedSemantic,
                reason: 'retention score below threshold' }

   Zero-eviction sweeps intentionally do NOT write an audit row (no
   state change, no need to flood the audit log during health checks).

2. The #124 scoring test only checked result.scores (transient
   response) but not the persisted mem:retention rows. Eviction reads
   back from stored rows, so a regression in kv.set/serialization
   would have still passed the old assertion. Now also does
   kv.get('mem:retention', id) and asserts { source: ... }.

Two new tests:
- Retention evict with a mixed set of 2 episodic + 1 semantic
  candidates writes exactly one audit row with all 3 ids in targetIds
  and the correct evictedEpisodic/Semantic breakdown in details.
- Retention evict with zero candidates writes zero audit rows.

Full suite: 730 passing (was 728 + 2 new).

* review: audit retention-score + parallelize writes (#124 round 4)

CodeRabbit round 3 outside-diff findings, both addressed:

1. mem::retention-score was persisting schema-relevant writes to
   KV.retentionScores (1000+ rows in a mature store) but never called
   recordAudit(). Per the repo audit-coverage policy, state-changing
   functions need an audit row. Added a single batched audit event
   per rescore:

     operation: 'retention_score' (new audit op — added to the
                                    AuditEntry union in types.ts)
     functionId: 'mem::retention-score'
     targetIds: [] (intentionally empty — a mature store can have
                    1000+ ids per sweep; flooding the audit log
                    with every memoryId on every cron tick is
                    worse than recording just the summary counts)
     details: { total, episodic, semantic, tiers, config }

   Zero-memory stores intentionally skip the audit call.

2. The per-memory kv.set inside the score loop was O(n) sequential
   round-trips. Refactored to collect pendingWrites: [id, entry][]
   while iterating, then flush with Promise.all at the end. On a
   mature store with 1000+ memories this is ~10x faster (depends on
   backend pipelining).

Test updates:
- Added 'mem::retention-score emits audit row per rescore' covering
  the new audit call, targetIds=[], and details.episodic/semantic.
- Existing '#124 audit evict' and 'zero-evict skip audit' tests now
  filter the audit log by functionId === 'mem::retention-evict'
  because retention-score also writes one row per sweep now.

Full suite: 731 passing (was 730 + 1 new, existing tests retargeted).
2026-04-15 12:06:43 +01:00
Rohit Ghumare f72c7a54fc fix: plugin auto-wires MCP + sandbox-safe skills (#139) (#141)
* fix: Claude Code plugin auto-wires MCP + sandbox-safe skills (#139)

Two fixes reported in #139:

1. The Claude Code plugin (rohitg00/agentmemory) only shipped hooks
   and skills; it did not auto-register any MCP server. Users had to
   configure the @agentmemory/mcp stdio server separately, which the
   README didn't make clear. Add plugin/.mcp.json so
   /plugin install agentmemory@agentmemory wires up the MCP server
   automatically when the plugin is enabled.

2. The `recall` and `session-history` skills used inline bash
   pre-execution blocks (`!$(curl -s ... ${VAR:-default})`) with
   shell expansion, which Claude Code's sandbox rejects by pattern
   match with "Contains expansion" — no way to bypass except for
   Claude to reconstruct the curl call manually, as the reporter
   had to work around.

   Rewrite all four plugin skills (recall / remember / forget /
   session-history) as pure prompts that tell Claude to use the MCP
   tools directly: memory_smart_search, memory_save, memory_sessions,
   memory_governance_delete. No bash, no sandbox issues, no shell
   escaping. Skills also run faster — no curl subprocess fork on
   every invocation.

Also tighten the README Claude Code install snippet to note that
the plugin now registers hooks + skills AND auto-wires the MCP
server in one step.

Bumps to 0.8.9. 707 tests still pass (no behavioral changes to
server code; only plugin assets).

* review: implement the MCP tools the new skills call (#139)

CodeRabbit flagged that the rewritten plugin skills reference
memory_smart_search and memory_governance_delete, but the
standalone MCP shim (@agentmemory/mcp) only exposed 5 tools:
memory_save, memory_recall, memory_sessions, memory_export,
memory_audit. At runtime the skills would have thrown
"Unknown tool" the first time they ran.

Add the missing handlers to src/mcp/standalone.ts:

- memory_smart_search — aliases memory_recall, falling back to
  substring search over the in-memory KV store. The standalone
  shim has no engine behind it, so BM25/vector/graph aren't
  available, but the substring match is good enough for plugin
  usage on small personal memory stores.
- memory_governance_delete — deletes memories by id. Accepts
  memoryIds as an array or a comma-separated string, returns
  {deleted, requested, reason}. Throws on missing/empty input,
  silently skips unknown ids.

Also normalize memory_save concepts/files: they now accept both
arrays (what the new skills send) and CSV strings (what the old
skills and legacy clients send). New normalizeList() helper
centralizes the logic.

Fix memory_sessions to honour a limit arg (default 20) — it was
previously returning every session in the store.

Update the forget skill wording per CodeRabbit's nit: make it
explicit that memory_governance_delete only takes memoryIds, and
that deleting a whole session means collecting all memory IDs
from that session via search first.

Adds 8 regression tests to test/mcp-standalone.test.ts covering:
- memory_save with array concepts/files
- memory_save with CSV concepts/files (legacy path)
- memory_smart_search substring fallback
- memory_sessions limit
- memory_governance_delete array path
- memory_governance_delete CSV path
- memory_governance_delete missing/empty validation
- memory_governance_delete unknown-id skip

Full suite: 715 passing (was 707 + 8 new).

* review: reject empty search query + fix skill troubleshooting paths (#139)

CodeRabbit round 2:

1. src/mcp/standalone.ts memory_smart_search accepted '' as a
   query and used .includes('') which matches every memory. In
   the forget flow this would surface unrelated memory IDs for
   deletion. Now throws 'query is required' on missing/empty/
   whitespace-only input. Also broadens the search corpus from
   just title+content to include files, concepts, sessionIds,
   and id — so users can /forget by file path, concept name, or
   session ID, as the skill docs already promise.

2. All four skill files (recall, remember, forget, session-history)
   said 'check the agentmemory engine is running on localhost:3111'
   when tools are missing. Wrong troubleshooting path: the stdio
   MCP shim spawned by Claude Code via .mcp.json has no REST API,
   no port, no localhost. The correct checks are /plugin list,
   /mcp, and restarting Claude Code after install. Updated all
   four skills.

Adds 2 new regression tests to test/mcp-standalone.test.ts:
- memory_smart_search rejects empty/whitespace/missing query
- memory_smart_search finds memories by file, concept, or id

Full suite: 717 passing.

* review: tighten content/limit validation in standalone MCP (#139)

CodeRabbit round 3:

1. memory_save cast args.content as string and called .trim(),
   which throws a runtime TypeError if content is a number, object,
   null, etc. Replace the cast with a proper typeof guard that
   returns the clean 'content is required' error for any non-string
   input.

2. memory_smart_search trimmed rawQuery for emptiness but then
   lowercased the *untrimmed* value, so a query like '  bcrypt  '
   would fail substring matches with trailing whitespace. Trim
   first, then lowercase.

3. (args.limit as number) || 10 is fragile — it treats 0 and NaN
   the same, silently accepts negative/Infinity values, and has no
   upper bound so a malicious or buggy caller could request a
   million memories in one slice(). Add parseLimit() helper that:
   - rejects non-number/non-string types (booleans, objects, arrays)
   - rejects NaN, Infinity, and non-positive values
   - clamps to MAX_LIMIT=100 via Math.min
   - Math.floor()s to force integer
   - falls back to a per-call default (10 for search, 20 for
     sessions, 50 for audit)

   Applied to all three limit sites: memory_smart_search,
   memory_sessions, memory_audit.

Adds 2 regression tests:
- memory_save rejects 42 / {} / [] / null / undefined / true without
  crashing on .trim()
- parseLimit falls back on bogus values and clamps 99999 -> 100

Full suite: 719 passing (was 717 + 2 new).
2026-04-14 17:03:58 +01:00
Rohit Ghumare 281e10ca8c fix: stop burning Claude tokens on every PostToolUse hook (#138) (#140)
* fix: stop burning Claude tokens on every PostToolUse hook (#138)

mem::observe was firing mem::compress unconditionally on every
PostToolUse hook, which called Claude via the user's ANTHROPIC_API_KEY
to compress each raw observation into a structured summary. On an
active coding session (50-200 tool calls/hour) this silently burned
hundreds of thousands of Claude API tokens within minutes. Reported
by @olcor1 on a fresh Docker install — his token allocation was gone
in 20 minutes.

Make per-observation LLM compression opt-in.

- New env var AGENTMEMORY_AUTO_COMPRESS, default `false`. When false,
  observe.ts builds a zero-LLM synthetic CompressedObservation from
  raw tool I/O (title from toolName, narrative from truncated
  input/output, files extracted from file_path/pattern/etc., type
  inferred from camelCase-normalized tool name) and stores + indexes
  it. Recall and BM25 search still work.
- New src/functions/compress-synthetic.ts with buildSyntheticCompression().
- src/functions/observe.ts gates the compress trigger on the flag and
  writes the synthetic observation + stream events when disabled.
- Startup banner now logs "Auto-compress: OFF (default, #138)" or a
  loud warning when opt-in is on, so the mode is never silent.
- Regression test: test/auto-compress.test.ts covers the default path
  (no mem::compress fired), explicit opt-in, tool-name-to-type
  mapping, file-path extraction, narrative truncation, and the
  post_tool_failure→error mapping.

Bumps to 0.8.8 (main package + shim). 707 tests pass.

**Migration**: users who were relying on LLM-generated summaries set
AGENTMEMORY_AUTO_COMPRESS=true in ~/.agentmemory/.env and restart.
Existing compressed observations on disk are untouched.

* review: address CodeRabbit findings on #140

- compress-synthetic.ts:92 — use the fallback toolName for inferType
  so both title and type derive from the same resolved value
- test/auto-compress.test.ts — vi.resetModules() in beforeEach to
  prevent cached observe.js from seeing stale env var state between
  the default, opt-in, and explicit-off test cases
2026-04-14 15:42:17 +01:00
Rohit Ghumare b864a7d268 fix: ship iii-config.docker.yaml in the npm tarball (#136) (#137)
docker-compose.yml mounts `./iii-config.docker.yaml:/app/config.yaml:ro`
but the `files` array in package.json only shipped `iii-config.yaml`.
Docker resolves missing bind sources by silently creating them as empty
directories, so `npx @agentmemory/agentmemory` mounted an empty dir at
/app/config.yaml and iii-engine crashed with:

  Error: Failed to read config file '/app/config.yaml':
  Is a directory (os error 21)

Fix: add iii-config.docker.yaml to package.json `files`.

Also adds a regression test in test/consistency.test.ts that parses
every `./<path>:<container>` bind mount in docker-compose.yml and
asserts the source file is shipped via the `files` array, so this
class of bug can't reach a release again.

Bumps main package + shim to 0.8.7 and adds CHANGELOG [0.8.7].
2026-04-14 09:58:19 +01:00
Rohit Ghumare 416fa15af4 chore: release 0.8.6 — scope the MCP shim as @agentmemory/mcp (#134)
0.8.5's publish workflow got blocked on the unscoped `agentmemory-mcp`
name by npm's name-similarity policy (there's an unrelated third-party
package called `agent-memory-mcp`). Publishing the shim under the
`@agentmemory` scope we already own sidesteps the conflict.

Changes:
- Rename packages/agentmemory-mcp → packages/mcp
- Package name: agentmemory-mcp → @agentmemory/mcp (0.8.4 → 0.8.6)
- Keep bin name `agentmemory-mcp` so `npm i -g @agentmemory/mcp`
  still installs a binary at `agentmemory-mcp` for muscle memory
- Log prefix [agentmemory-mcp] → [@agentmemory/mcp] in standalone.ts
  and in-memory-kv.ts
- README / integrations/openclaw / integrations/hermes / src/cli.ts
  all point at `npx -y @agentmemory/mcp`
- .github/workflows/publish.yml working-directory and npm view queries
  target packages/mcp and @agentmemory/mcp
- Shim dependency on @agentmemory/agentmemory bumped to ~0.8.6
- Main package 0.8.5 → 0.8.6 across the 8 version files
- CHANGELOG [0.8.6] entry documenting the scope move
2026-04-14 00:31:10 +01:00
Rohit Ghumare 3935aecdae chore: bump version to 0.8.5 + add CHANGELOG (#133) 2026-04-13 20:03:04 +01:00
Rohit Ghumare 95e089a37c chore: release 0.8.4
Two community contributions on top of 0.8.3 land as a patch release so
they reach users immediately and so `npx agentmemory-mcp` ships to the
npm registry for the first time (closing out #120 on the live release).

- #122 (Jason Landbridge): memory_save persists to disk immediately
  after every call, not just on SIGINT/SIGTERM, so memories survive
  SIGKILL from agent sessions ending forcefully
- #121 (Jason Landbridge): OpenCode MCP config format corrected —
  opencode.json with mcp/type:local/command array instead of the
  wrong .opencode/config.json with mcpServers

Version bumped across package.json, src/version.ts, types.ts,
export-import supportedVersions, plugin.json, shim package.json and
its dependency on the main package (~0.8.4). package-lock.json
regenerated. 684 tests passing, clean build.
2026-04-13 12:35:35 +01:00
Rohit Ghumare 8a36fe7867 fix: retention scoring (#119) + npx agentmemory-mcp 404 (#120), release 0.8.3 (#123)
* fix: retention scoring (#119) + npx agentmemory-mcp 404 (#120), release 0.8.3

Issue #119 — retention score ignored agent-side reads

`mem::retention-score` previously hardcoded `accessCount=0` and
`accessTimestamps=[]` for episodic memories and used only a single-sample
`lastAccessedAt` for semantic memories, so the time-frequency decay
formula was a dead path. All read endpoints now record access events to
a new `mem:access` KV namespace via a keyed-mutex'd access tracker:

  - mem::search, mem::smart-search (compact + expand), mem::context,
    mem::timeline, mem::file-context, mem::get-related, mem::working-context
  - retention.ts pre-fetches the full access log in one kv.list call,
    normalizes every row defensively, and builds an O(1) lookup Map
  - recordAccessBatch uses Promise.allSettled so a slow keyed-lock on one
    id does not block the rest of the batch
  - Bounded ring buffer of the last 20 access timestamps per memory
  - Backwards-compat fallback: pre-0.8.3 semantic memories with only the
    legacy sem.lastAccessedAt still score correctly
  - Corrupt lastAccessedAt values are NaN-safe via Date.parse + isFinite
  - reinforcementBoost is reported as the raw formula output, not the
    clamped residual

Memory deletion sites (retention-evict, evict, auto-forget,
governance-delete, governance-bulk, forget, import replace) now call
deleteAccessLog to prevent mem:access from accumulating zombie entries.
mem::export / mem::import and mem::snapshot-create / mem::snapshot-restore
round-trip the new namespace so backup/restore cycles no longer zero out
reinforcement signals.

Issue #120 — npx agentmemory-mcp returned npm registry 404

The README instructed users to run `npx agentmemory-mcp`, but
`agentmemory-mcp` only existed as a `bin` entry inside
`@agentmemory/agentmemory`, not a real registry package. Two-part fix:

  - New sibling package `packages/agentmemory-mcp/` — a thin shim that
    depends on `@agentmemory/agentmemory` (~0.8.3, tilde to block 0.9.x
    supply-chain drift) and forwards to dist/standalone.mjs via the new
    `exports` field on the main package
  - Canonical `npx @agentmemory/agentmemory mcp` subcommand on the CLI
    for users who already have the main package installed
  - Removed the dead `agentmemory-mcp` bin from the main package so the
    shim owns the name unambiguously
  - publish.yml publishes both packages in order with idempotent
    guards and npm-view registry polling (no more fixed sleep)
  - All MCP install snippets in README + integrations/openclaw + hermes
    + shim README use `npx -y agentmemory-mcp` to skip install prompts

Release 0.8.3

  - 670 tests passing (665 + 11 new: access tracker, retention,
    smart-search integration, corrupt-input, kv.list failure path,
    Promise.allSettled resilience)
  - Version bumped across package.json, src/version.ts, types.ts,
    export-import supportedVersions, plugin.json
  - package-lock.json regenerated

* fix: address CodeRabbit review feedback on #123

Real issues caught and fixed:

- access-tracker: deleteAccessLog now takes the same mem:access:<id>
  keyed mutex used by recordAccess, eliminating the race where a
  concurrent delete could drop an in-flight RMW update and leave a
  stale access log for an evicted memory
- access-tracker: normalizeAccessLog now coerces count to a
  non-negative integer, drops non-finite recent[] entries, truncates
  recent[] to the last 20 on read (enforcing the ring-buffer invariant),
  and guarantees count >= recent.length so a malformed row can't produce
  NaN downstream. count is NOT capped at 20 — it is the lifetime access
  counter, while recent[] is the 20-entry window
- export-import: import path now rejects accessLogs arrays above
  MAX_ACCESS_LOGS (50_000), normalizes each entry via
  normalizeAccessLog, and drops entries whose memoryId is not in the
  imported memories set, so a crafted export can't blow up memory or
  resurrect zombie access rows for memories that don't exist
- publish.yml: added a post-publish npm view polling loop for
  agentmemory-mcp mirroring the one that already guards the main
  package, so the workflow only succeeds when `npx agentmemory-mcp` is
  actually visible on the registry
- CHANGELOG.md: added the missing [0.8.3] reference-style link target
  at the bottom to match the [0.8.2]/[0.8.1]/[0.8.0] pattern
- README.md: standalone MCP inline examples now use `npx -y` to match
  the MCP config snippets and skip the install confirmation prompt

Tests: 680/680 (+10 new — deleteAccessLog behavior, normalizeAccessLog
invariants, malformed mem:access row handling in retention scoring).

Findings deliberately not applied:

- auto-forget.ts / remember.ts / evict.ts recordAudit calls: existing
  pattern in the codebase only audits governance deletes (user-initiated
  admin actions), not automatic eviction. Adding audit everywhere is a
  policy change, out of scope for this PR
- evict.ts gating deleteAccessLog on memory delete success: current
  order is defensible, deleteAccessLog is already best-effort with its
  own try/catch, and calling it after a failed memory delete is harmless
- snapshot.ts fail-fast on kv.list failure: inconsistent with the
  existing .catch(() => []) pattern used for observations in the same
  function
- retention.ts semantic evict only deletes KV.memories rows: real bug
  but pre-existing on main (RetentionScore has no source/type field).
  Tracked separately — out of scope for this PR
- bin.mjs fallback command: already present in the existing catch
  handler
- Promise.all parallelization of delete+deleteAccessLog: micro
  optimization not worth the readability cost
2026-04-13 12:05:18 +01:00
Rohit Ghumare fa2aa980fb chore: bump version to 0.8.2 + add CHANGELOG (#117)
## Version bumped across
- package.json + package-lock.json
- plugin/.claude-plugin/plugin.json
- src/version.ts (union type)
- src/types.ts ExportData.version union
- src/functions/export-import.ts supportedVersions set
- test/export-import.test.ts assertion

Also fixed the ExportData version union syntax: prior releases used
commas (`| "0.7.9", "0.8.0", "0.8.1"`) instead of pipes, which would
not work as intended. Fixed to a proper pipe-separated union.

## CHANGELOG.md

New file documenting v0.8.2 in Keep-a-Changelog format. Sections:

**Security (6 fixes)** — addresses CVEs in default deployments:
- [CRITICAL] Stored XSS in viewer
- [CRITICAL] curl|sh in CLI startup
- [HIGH] Default 0.0.0.0 binding
- [HIGH] Unauthenticated mesh sync
- [MEDIUM] Path traversal in Obsidian export
- [MEDIUM] Incomplete secret redaction

**Added**:
- agentmemory demo CLI command
- benchmark/COMPARISON.md
- integrations/openclaw/ gateway plugin
- Token savings dashboard in CLI + viewer
- Paste-this-prompt blocks
- 60 custom SVG tags (30 dark + 30 light variants)
- Real agent logos in Supported Agents grid

**Changed**:
- README redesigned with SVG tags, brand palette
- Supported Agents grid reordered
- Default ports bound to 127.0.0.1
- Hero stat row uses custom SVG cards

**Fixed**:
- Viewer cost calculation 100x underreporting
- ObservationType union missing "image"
- Dynamic imports inside eviction loops
- OpenClaw /context payload contract
- Cursor cell missing label
- Codex logo 404

**Infrastructure**:
- 654 tests (up from 646), 8 new security tests
- All 60 SVGs validated
- README consistency check updated
2026-04-12 20:38:08 +01:00
tanmaishi e692cf0095 feat: implement multimodal image memory 2026-04-11 11:38:30 +05:30
Rohit Ghumare 7af05ecf78 chore: bump version to 0.8.1 2026-04-09 19:10:52 +01:00
Rohit Ghumare 798700f2a3 chore: bump version to 0.8.0 2026-04-09 09:07:57 +01:00
Rohit Ghumare 29cbaa7624 chore: bump version to 0.7.9 2026-04-08 10:52:30 +01:00
Rohit Ghumare e6366e5c03 fix: skills path ./skills/ instead of ../skills/ (#94)
Claude Code resolves skill paths relative to the plugin root (the
directory containing .claude-plugin/), not relative to plugin.json.
2026-04-08 10:51:56 +01:00
Rohit Ghumare 805620aca1 chore: bump version to 0.7.8 2026-04-08 10:28:11 +01:00
Rohit Ghumare 740c7b90a1 fix: plugin hooks and skills validation errors (#100)
- hooks.json: wrap each hook in { hooks: [{ type, command }] } format
  (Claude Code expects nested hooks array, not flat type/command)
- plugin.json: change skills from explicit file paths to directory
  reference ["../skills/"] matching working plugin convention
2026-04-08 10:27:25 +01:00
Rohit Ghumare 8704131745 chore: bump version to 0.7.7 2026-04-07 19:56:12 +01:00
Rohit Ghumare fbded55b29 fix: plugin.json validation errors (#94)
- author: change from string to object {name, url}
- hooks: remove field (auto-loaded by convention from hooks/hooks.json)
- skills: change from directory string to explicit file path array
2026-04-07 19:54:13 +01:00
Rohit Ghumare b81682da8b chore: bump version to 0.7.6 2026-04-07 19:43:17 +01:00