* chore(release): v0.9.29 with project-scope parity across surfaces
Version trio + plugin manifests + supportedVersions + ExportData union
bumped to 0.9.29; CHANGELOG entry covering everything since v0.9.28 with
upgrade notes for the four visible behavior changes.
Fixes the endpoint-count drift on main (130 registered routes vs docs
saying 129 after #1132 landed in parallel with #1136).
Project-scope parity: OpenCode plugin, Hermes plugin, Pi extension, and
JSONL replay now resolve project the same way the hooks do (env
override, git toplevel basename, cwd basename) instead of sending raw
filesystem paths, closing #903 and #1135 and pre-empting the same bug
in pi. The filesystem watcher accepts AGENTMEMORY_PROJECT_NAME with the
old AGENTMEMORY_PROJECT kept as a deprecated alias, replay handles
Windows-recorded paths, and OpenCode file enrichment matches the
agent's lowercase tool names (the capitalized set never matched).
Tests: opencode fallback expectations updated to basenames per the
canonicalization, git-toplevel resolution covered with a fixture repo,
new project-scope-parity suite for replay and fs-watcher.
* fix(release): review findings, git-toplevel parity, doc counts
- skills generator dedupes routes on method plus path, so the REST
reference lists all 130 registered routes instead of hiding the second
method on ten dual-method paths (header said 119)
- fs-watcher trims AGENTMEMORY_PROJECT_NAME and the deprecated alias,
treating whitespace as unset, and derives the git toplevel basename
when watching a subdirectory
- replay resolves the git toplevel basename when the recorded cwd still
exists locally (memoized per cwd), keeping the basename fallback for
historical or cross-platform paths; no env override here since a bulk
import spans many projects
- parity tests for replay git-root resolution, watcher git-root and
trim behavior
- stat-tests badge updated from 1428+ to 1550+ passing
* fix(cli): refuse second-instance boot over a live daemon
Closes the class behind issue 1140: agentmemory consolidate (or any
unrecognized word) fell through the command table into the full server
boot, registering a duplicate worker on the running engine; on iii
0.11.2 the second instance's shutdown tears down the daemon's HTTP
trigger routing until a full engine restart. Unknown subcommands now
error with the supported list, and main() probes livez on the resolved
port and refuses to boot over a live daemon, so multi-instance setups
on other ports are unaffected. Verified behaviorally against the built
CLI: both paths refuse with exit 1.
Also from review: the watcher stamps each event with its own root's
project via a per-root map (an explicit config.project still overrides
for every root), and replay only accepts a non-empty string cwd from
parsed JSONL so malformed entries cannot reach the filesystem probe.
* test(watcher): two-repository flush events scope to their own project
* chore(release): bump packages/mcp, guard it, refresh CONTRIBUTING
packages/mcp was still 0.9.28 after the release bump because nothing
guarded it; a consistency test now pins it to package.json. CONTRIBUTING
release list corrected to the files a bump actually touches (no tracked
lockfile, the two extra plugin manifests, the export test derives from
VERSION now), and the subsystems table gains src/cli, integrations/pi,
and the generated-manifest note.
* fix(export): refuse over-frame export instead of dropping the worker
Closes the availability bug in issue 1142: GET /agentmemory/export
assembles the full store and returns it through sdk.trigger, so a store
whose serialized export passes the engine's 16 MiB WebSocket frame
(tungstenite max_frame_size, not raisable under the 0.11.2 pin) dies on
the worker->engine hop, drops the worker, and 404s every endpoint for
~1s. The session collections page on maxSessions/offset but ~18 others
do not, so a large store hits this at any parameter combination.
A shared frame-guard measures the serialized size before returning:
mem::export returns a small oversized error instead of the giant
object, and api::mesh-export returns 413 (same dead-end as #890). Either
way the over-frame payload never crosses the boundary, so the daemon
stays up and the failure is one clean request with a hint to narrow the
range. Full pagination of the non-session collections is a follow-up.
Layer 1 of the fix; verified with a synthetic oversized export returning
the error object (tiny) rather than the payload.
* ci: collapse to a single npm install to fix Node 24/26 CI
The two-step install (npm install --package-lock-only then npm ci) failed
only on the Node 24/26 matrix rows: their stricter npm rejects rolldown's
optional platform bindings (@rolldown/binding-android-arm64) that a
--package-lock-only pass does not fully enumerate. Lockfiles are gitignored,
so npm ci re-validation buys no reproducibility here. A single lenient
npm install resolves and installs in one pass.
* fix(mesh): scope exported memories by project like actions
api::mesh-export filtered actions by ?project but returned every project's
memories. On a mesh instance federating one project to a peer, the peer
pulled other projects' memories (cross-project leak), and those extras could
push the payload past the 16 MiB transport frame into a 413 even when the
requested project's own slice fit. Memories carry the same optional project
field as actions, so filter both before the frame-size guard runs.
Adds a regression test asserting a project-scoped export excludes other
projects' memories and that an oversized memory in another project no longer
413s the scoped request.
* chore(release): credit the Antigravity native hooks adapter in 0.9.29 notes
* chore(release): sweep stale 0.9.28 refs for 0.9.29
Deploy Dockerfiles/compose/render pins, AGENTS.md stats header, opencode
plugin manifest, website meta snapshot, test-count claims (1,428 -> 1,596)
in README/AGENTS/stat SVGs, and the missing 0.9.29 CHANGELOG compare link.
* chore(release): sync stat-tests badge to 1596+ and commit bridge exec bit
* refactor: trim frame-guard comments and drop issue refs from code
* fix(memory): guard mem::forget delete/count on record existence
Calling mem::forget with a lesson id (lsn_*) deleted a nonexistent key
from the memories keyspace, counted it, and reported success. Guard the
delete, index cleanup, and counter on the kv.get result, matching the
mem::governance-delete pattern, so nonexistent ids return
{ success: true, deleted: 0 } with no audit row. Closes#1120.
* feat(lessons): add mem::lesson-delete soft-delete function
Register mem::lesson-delete to set deleted: true on a lesson, mirroring
the lesson-strengthen existence guard and audit pattern. Read paths
already filter !l.deleted, and re-saving deleted content creates a fresh
lesson. Adds lesson_delete to the audit operation union.
* feat(mcp): expose memory_lesson_delete tool and REST endpoint
Wire mem::lesson-delete through the MCP tool registry and dispatch
case (memory_lesson_delete) and a POST /agentmemory/lessons/delete REST
route with 400 for a missing lessonId and 404 for a nonexistent lesson.
* chore(consistency): bump tool/endpoint counts to 54/129
Adds memory_lesson_delete to the registry, so update every count surface:
tool-count test, README badge and prose, AGENTS.md stats,
INSTALL_FOR_AGENTS.md, plugin manifests and docs, and the two code
comments this change makes stale. REST endpoint count goes 128 to 129
for the new /agentmemory/lessons/delete route.
* refactor(lessons): simplify 404 mapping and restore decay-delta test
Cast the lesson-delete trigger result once instead of twice inline, and
restore the lastDecayedAt incremental-delta decay test that was dropped
when the lesson-delete describe block was added.
* fix(review): align 404 error shape and regenerate skill references
Review fixes: the lesson-delete REST route now returns the repo-standard
{ error: 'lesson not found' } body on 404 instead of the function-shaped
{ success: false } payload, matching api::memory-by-id. Regenerated the
autogen MCP and REST skill references so memory_lesson_delete and the
lessons/delete route appear in the tables with accurate counts.
* fix(lessons): normalize lessonId at entry points and harden no-op test
Address CodeRabbit review: trim lessonId once at both the MCP dispatch
and REST route before triggering mem::lesson-delete (whitespace-padded
ids previously 404'd or looked up raw), and extend the nonexistent-
memoryId regression test to assert the no-op path performs no kv.delete
and no search-index cleanup.
---------
Co-authored-by: Rohit Ghumare <48523873+rohitg00@users.noreply.github.com>
#1047: JSON.parse("null") returns null without throwing, so every hook's parse guard passed it through and the first data.xxx access threw a TypeError. Bare main() turned that into an unhandled rejection -> exit 1 -> host reported 'hook failed' on every affected tool call. All 13 hook entrypoints now guard non-object payloads before dereferencing and wrap main() in .catch() to fail closed (silent exit 0).
#1057: mem::context and api::context filtered candidate sessions by project only, leaking cross-agent observations/summaries under AGENTMEMORY_AGENT_SCOPE=isolated. Now applies the same agent-scope filter as mem::search (#817); api::context, api::session::start, and event::session::started forward agentId.
Also: bump 0.9.28 across manifests/deploy/export-import set; refresh stale README/AGENTS stats (files/LOC/functions/KV; AGENTS tests 950+ -> 1,428+) and regenerate the website meta snapshot to 0.9.28; CHANGELOG 0.9.28 section; remove the rate-limited star-history chart from README and all 11 translations.
First boot after upgrading to v0.9.25 threw
'TypeError: Cannot read properties of undefined (reading v)' from
IndexPersistence.loadManifestData. Some iii-state adapters return
undefined (not null) for a missing key. The load path checked
manifest.value !== null and forwarded undefined to loadManifestData,
which then read undefined.v.
Self-healing: the next debounced save rebuilt a fresh manifest, so
operation continued after the warning — but the error scared every
fresh upgrader.
Fix: treat null + undefined + non-object values as 'no manifest' and
fall through to the legacy load path. Two regression tests:
- kv.get returning undefined surfaces as a clean { bm25: null,
vector: null } load, no throw.
- kv.get returning a string (wrong shape) does not crash the
manifest path.
Files bumped (9):
package.json, plugin/.claude-plugin/plugin.json, plugin/plugin.json,
plugin/.codex-plugin/plugin.json, src/version.ts, src/types.ts,
src/functions/export-import.ts, test/export-import.test.ts,
packages/mcp/package.json
125 test files / 1381 tests pass.
Closes#797.
Two bugs surfaced in the first hour after v0.9.23 hit npm:
1. `agentmemory --version` silently launched the server instead of
printing the version. `-v` was reserved by --verbose and no
--version literal was handled — fell through to default `start`.
Now --version (and -V capital, POSIX) prints VERSION + exits 0
before any side effects.
2. iii-engine version pin was warn-only. When the engine on PATH
didn't match agentmemory's pinned v0.11.2, the worker still booted
against the mismatched engine and crashed at runtime with
state::list not found (v0.13.0) or sandbox traps (v0.11.6).
warnIfEngineVersionMismatch renamed to enforceEngineVersionPin
and now p.log.errors + process.exit(1). AGENTMEMORY_III_VERSION
override unchanged.
* chore(release): v0.9.23
Bumps version across 9 files + adds CHANGELOG entry summarizing the
18 commits since v0.9.22.
Highlights:
- GitHub Copilot CLI first-class support (#534) — plugin + hooks +
MCP with LSP-style Content-Length framing on the standalone stdio
transport.
- Five new MCP adapters: Warp, Cline, Continue, Zed, Droid (#677);
ADAPTERS count 11 → 17.
- Three silent DX bugs fixed: graph extraction never fired on
session end (#666 / #698), status reported zero memories (#666),
consolidation defaulted off even with an LLM provider configured
(#612 / #696).
- Nine telemetry hooks switched to fire-and-forget so they don't
block Claude Code's next-prompt boundary (#573 / #688).
- Hook project field now sends repo basename instead of full
filesystem path so auto-injected context isn't silently filtered
out (#474 / #687).
- Local-LLM docs: Ollama / LM Studio / vLLM section added (#671 /
#697).
Version-bump files: package.json, plugin/.claude-plugin/plugin.json,
plugin/plugin.json, plugin/.codex-plugin/plugin.json,
packages/mcp/package.json, src/version.ts, src/types.ts,
src/functions/export-import.ts, test/export-import.test.ts.
* chore(release): add #701 + #709 to v0.9.23 CHANGELOG
v0.9.19 shipped #495 which chained session-end.mjs after stop.mjs on
the Codex Stop hook. Field-testing surfaced the underlying issue:
Codex fires Stop multiple times within a single conversation (once
per assistant turn), so chaining session-end marked sessions as
completed while later observations were still arriving.
#501 reverts the chain. Stop returns to summarize-only behavior. The
SessionEnd-shaped solution (a dedicated terminate event the agent
sends only once on real session end) tracks at #493.
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
1034/1034 tests pass.
`install.iii.dev/console/main/install.sh` filters releases with
`startswith("v")` while `iii-hq/iii` tags as `iii/v0.12.0` —
slash-prefixed. Every stable release is filtered out and the script
bails with "no stable iii release found", breaking first-run console
install for every fresh agentmemory user.
Switch our prompt to pass `--next` through the installer. The next-
release lookup uses a regex on `-next.` without the buggy
`startswith` constraint, so it succeeds against the same tag set and
installs the most recent prerelease. Inline comment documents the
upstream tag-prefix bug + the revert condition for when the
installer is patched.
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
1007/1007 tests pass.
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.
* 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).
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.
* 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.