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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,13 @@ jobs:
- uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
# Two-step install: generate a lockfile in-runner with
# --package-lock-only, then install from it with `npm ci`.
# Lockfiles are gitignored at the repo level.
- run: npm install --package-lock-only --legacy-peer-deps --no-audit --no-fund
- run: npm ci --legacy-peer-deps --no-audit --no-fund
# Lockfiles are gitignored, so `npm ci` (which strictly re-validates a
# committed lockfile) buys no reproducibility here — and Node 24+'s
# stricter npm rejects rolldown's optional platform bindings that a
# `--package-lock-only` pass doesn't fully enumerate, failing the matrix
# on 24/26 only. A single lenient `npm install` resolves and installs
# in one pass.
- run: npm install --legacy-peer-deps --no-audit --no-fund
- run: npm run build
- run: npm run skills:check
- run: npm test
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,16 +109,16 @@ Hook scripts in `src/hooks/` are standalone Node.js scripts (no iii-sdk import).

## Testing

- All tests must pass before PR: `npm test` (1,428+ tests)
- All tests must pass before PR: `npm test` (1,596+ tests)
- Mock pattern: `vi.mock("iii-sdk")` with mock `sdk.trigger`, `kv.get/set/list`
- Test files go in `test/` with `.test.ts` extension
- Follow existing patterns in `test/crystallize.test.ts` for function tests

## Current Stats (v0.9.28)
## Current Stats (v0.9.29)

- 54 MCP tools (8 visible by default, `AGENTMEMORY_TOOLS=all` for all)
- 129 REST endpoints
- 130 REST endpoints
- 6 MCP resources, 3 MCP prompts
- 12 hooks, 15 skills
- 260+ iii functions
- 1,428+ tests
- 1,596+ tests
46 changes: 46 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,51 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

## [Unreleased]

## [0.9.29] — 2026-08-02

Patch release: the `.env` file now actually applies everywhere, imports become searchable, consolidation runs on session stop, twelve MCP-only agents get activated on connect, and every capture surface finally agrees on what "project" means. No breaking changes; read the upgrade notes below for four behavior changes you will notice.

### Upgrade notes

- `~/.agentmemory/.env` values that were silently ignored by most modules now take effect on boot. If that file has stale entries from past experiments, review it before upgrading.
- `agentmemory connect <agent>` now writes a short memory-usage guideline into the agent's native rules file (Cursor, Cline, Continue, Zed, Warp, Kiro, Gemini CLI, Qwen, OpenCode, Droid, Copilot CLI, Antigravity) so MCP-only agents actually call the memory tools. Pass `--no-guidelines` to opt out.
- Installs with an LLM key now run consolidation and crystallization on session stop (previously they never fired), debounced to once per 5 minutes (`AGENTMEMORY_CONSOLIDATION_COOLDOWN_MS`).
- Local embeddings re-download once after the `@huggingface/transformers` migration (different model cache directory). Model IDs are unchanged.

### Added

- `--data-dir` flag and `AGENTMEMORY_DATA_DIR` so iii-engine state lives outside repositories, with gated legacy `./data` adoption and Docker-volume preservation (#314)
- Native hooks adapter for Droid via `~/.factory/hooks.json`, reusing the bundled hook scripts (#1130)
- Native hooks adapter for Antigravity CLI (agy) via a stdin bridge that normalizes agy's hook payloads onto the bundled hook scripts, with an explicit PreToolUse allow decision (#1146, thanks @berthojoris)
- `mem::graph::import-graphify` and `POST /agentmemory/graph/import-graphify`: merge graphify's `graph.json` into the knowledge graph with confidence tags carried over as edge weights (#1136)
- Connector guideline activation for twelve hook-less agents, with every rules-file path verified against the agent's official documentation (#1136)
- Honest `memory_forget` reporting plus a real lesson delete path (`mem::lesson-delete`, `DELETE`-style REST route, MCP tool) (#1132)
- `AGENTMEMORY_PROJECT_NAME` override in the OpenCode plugin (#1125)
- Provider fetches retry 429/503 honoring `Retry-After` under a total-elapsed budget capped below the iii invocation timeout (#1136)

### Fixed

- Boot hydrates `~/.agentmemory/.env` into `process.env`, closing the class of "env var in .env is ignored" bugs (#1136)
- Imported and replayed observations are indexed into BM25 and the vector index, so imports are searchable (#1072, via #1136)
- Snapshot timer actually runs, non-positive intervals clamp to the default, and snapshot creation is serialized across timer, REST, and MCP (#1006, via #1136)
- CJK-aware dedup with NFC normalization and an exact-match fallback for short memories (#1021, via #1136)
- OpenRouter embeddings no longer hardcode 1536 dimensions (#1002, via #1136)
- Viewer decodes multibyte request bodies correctly (#930, via #1136)
- Session-stop consolidation is debounced and no longer double-fires from the client hook; eviction recovery is bounded to one consolidation pass (#1087, #1131 class, via #1136)
- `/agentmemory/sessions` no longer deadlocks on large session counts (#1100, via #1136)
- Filesystem watcher validates roots before `fs.watch`, fixing Node 24/26 on Linux (#1136)
- `GET /agentmemory/export` and `/agentmemory/mesh/export` refuse an over-frame response instead of shipping it: a payload past the engine 16 MiB transport frame used to drop the worker and 404 every endpoint for ~1s. They now fail that one request (413 for mesh, an `oversized` error for export) with a hint to narrow the range, keeping the daemon up (#1142, #890). Full pagination of the non-session collections is a follow-up.
- Claude bridge writes `MEMORY.md` under the `memory/` subdirectory Claude Code actually reads (#1134)
- Hook project-resolution tests no longer depend on the checkout directory name (#1137, #1138)
- Project-scope parity: the 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, so the same repository shares one memory bucket across agents (#903, #1135); the filesystem watcher accepts `AGENTMEMORY_PROJECT_NAME` with the old `AGENTMEMORY_PROJECT` kept as a deprecated alias; replay handles Windows-recorded paths
- OpenCode file enrichment matches the agent's lowercase tool names, which the previous capitalized set never did
- Viewer surfaces health status from non-2xx health responses (#1046)
- Documented REST endpoint count matches the registered routes again (130)

### Changed

- Local embeddings migrate from `@xenova/transformers` to `@huggingface/transformers` v4 with Node 22+ support; CI now tests Node 20, 22, 24, and 26 (#479, #1096)

## [0.9.28] — 2026-07-19

Patch release: hardens the hook runner against malformed payloads and closes a cross-agent context leak. No breaking changes; drop-in upgrade.
Expand Down Expand Up @@ -66,6 +111,7 @@ Wave release closing several breaking regressions reported against v0.9.26, plus
- `/agentmemory:forget` skill still calls `memory_governance_delete` which only touches `KV.memories` and never observations ([#833](https://github.com/rohitg00/agentmemory/issues/833)). Skill rewrite + new `memory_forget` MCP tool tracked separately.
- `crypto.randomUUID()` global-only on Node <19 ([#715](https://github.com/rohitg00/agentmemory/issues/715)). Drop-in import fix tracked.

[0.9.29]: https://github.com/rohitg00/agentmemory/compare/v0.9.28...v0.9.29
[0.9.28]: https://github.com/rohitg00/agentmemory/compare/v0.9.27...v0.9.28
[0.9.27]: https://github.com/rohitg00/agentmemory/compare/v0.9.26...v0.9.27

Expand Down
23 changes: 13 additions & 10 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,11 @@ PRs with commits lacking sign-off will not merge.
| `src/mcp/` | Standalone MCP server (`@agentmemory/mcp`), tools registry, transport, in-memory KV. |
| `src/functions/` | Core memory operations — observe, compress, consolidate, retention, forget, graph, smart-search, export-import, governance. |
| `src/hooks/` | The 12 auto-hooks that capture sessions in agents. |
| `src/cli/` | The `agentmemory` CLI, including `connect/` adapters for 18 agents and the guideline writer for hook-less agents. |
| `src/health/` | Liveness + readiness + alert thresholds. |
| `src/state/` | KV schema, keyed mutex, access log. |
| `integrations/` | First-party plugins: `hermes/`, `openclaw/`, `filesystem-watcher/`. |
| `plugin/` | Claude Code plugin (`agentmemory@agentmemory`). |
| `integrations/` | First-party plugins: `hermes/`, `openclaw/`, `pi/`, `filesystem-watcher/`. |
| `plugin/` | Agent plugin bundle: Claude Code plugin, hook manifests for Codex/Copilot/Droid, the OpenCode capture plugin, and the skills. Hook manifests and skill REFERENCE files are partly generated; run `npm run skills:gen` after touching registered endpoints or env vars. |
| `website/` | Marketing site (Next.js 16). |
| `test/` | Vitest test suite. |

Expand All @@ -92,18 +93,20 @@ PRs with commits lacking sign-off will not merge.

## Release process

Maintainers cut releases. Every bump touches 8 files in lockstep:
Maintainers cut releases. Every bump touches these files in lockstep (the consistency tests fail if the trio of doc counts or any version drifts):

1. `package.json`
2. `package-lock.json` (top + `packages[""].version`)
2. `src/version.ts`
3. `plugin/.claude-plugin/plugin.json`
4. `packages/mcp/package.json` (self + `~x.y.z` pin on the main package)
5. `src/version.ts` (extend the union, assign)
6. `src/types.ts` (`ExportData.version` union)
7. `src/functions/export-import.ts` (`supportedVersions` Set)
8. `test/export-import.test.ts` (assertion)
4. `plugin/plugin.json`
5. `plugin/.codex-plugin/plugin.json`
6. `packages/mcp/package.json`
7. `src/types.ts` (`ExportData.version` union)
8. `src/functions/export-import.ts` (`supportedVersions` Set)

Then: CHANGELOG section, PR, merge, tag, GitHub release. The `Publish to npm` workflow picks up the release trigger and publishes `@agentmemory/agentmemory`, `@agentmemory/mcp`, and `@agentmemory/fs-watcher` to npm with provenance.
No lockfiles are committed. `test/export-import.test.ts` asserts against the `VERSION` constant, so it needs no per-release edit. Run `npm run skills:gen` if the endpoint or env surface changed.

Then: CHANGELOG section, PR, merge, tag, GitHub release. The `Publish to npm` workflow picks up the release trigger and publishes `@agentmemory/agentmemory`, `@agentmemory/mcp`, and `@agentmemory/fs-watcher` to npm with provenance (`@agentmemory/fs-watcher` versions independently from `integrations/filesystem-watcher/package.json`).

## Security issues

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
<picture><source media="(prefers-color-scheme: dark)" srcset="assets/tags/light/stat-tools.svg"><img src="assets/tags/stat-tools.svg" alt="54 MCP tools" height="38" /></picture>
<picture><source media="(prefers-color-scheme: dark)" srcset="assets/tags/light/stat-hooks.svg"><img src="assets/tags/stat-hooks.svg" alt="12 auto hooks" height="38" /></picture>
<picture><source media="(prefers-color-scheme: dark)" srcset="assets/tags/light/stat-deps.svg"><img src="assets/tags/stat-deps.svg" alt="0 external DBs" height="38" /></picture>
<picture><source media="(prefers-color-scheme: dark)" srcset="assets/tags/light/stat-tests.svg"><img src="assets/tags/stat-tests.svg" alt="1,428+ tests passing" height="38" /></picture>
<picture><source media="(prefers-color-scheme: dark)" srcset="assets/tags/light/stat-tests.svg"><img src="assets/tags/stat-tests.svg" alt="1,596+ tests passing" height="38" /></picture>
</p>

<p align="center">
Expand Down Expand Up @@ -1209,7 +1209,7 @@ Full registry: [workers.iii.dev](https://workers.iii.dev). Every worker there co
| Prometheus / Grafana | iii OTEL + health monitor |
| Custom plugin systems | `iii worker add <name>` |

**175 source files · ~39,200 LOC · 1,428+ tests · 261 functions · 52 KV scopes** — all on three primitives. No `agentmemory plugin install`. The plugin system is iii itself.
**175 source files · ~39,200 LOC · 1,596+ tests · 261 functions · 52 KV scopes** — all on three primitives. No `agentmemory plugin install`. The plugin system is iii itself.

---

Expand Down Expand Up @@ -1499,7 +1499,7 @@ Create `~/.agentmemory/.env`:

<h2 id="api"><picture><source media="(prefers-color-scheme: dark)" srcset="assets/tags/light/section-api.svg"><img src="assets/tags/section-api.svg" alt="API" height="32" /></picture></h2>

129 endpoints on port `3111`. The REST API binds to `127.0.0.1` by default. Protected endpoints require `Authorization: Bearer <secret>` when `AGENTMEMORY_SECRET` is set, and mesh sync endpoints require `AGENTMEMORY_SECRET` on both peers.
130 endpoints on port `3111`. The REST API binds to `127.0.0.1` by default. Protected endpoints require `Authorization: Bearer <secret>` when `AGENTMEMORY_SECRET` is set, and mesh sync endpoints require `AGENTMEMORY_SECRET` on both peers.

<details>
<summary>Key endpoints</summary>
Expand Down Expand Up @@ -1533,7 +1533,7 @@ Full endpoint list: [`src/triggers/api.ts`](src/triggers/api.ts)
```bash
npm run dev # Hot reload
npm run build # Production build
npm test # 1,428+ tests
npm test # 1,596+ tests
npm run test:integration # API tests (requires running services)
```

Expand Down
4 changes: 2 additions & 2 deletions assets/tags/light/stat-tests.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions assets/tags/stat-tests.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion deploy/coolify/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ FROM iiidev/iii:${III_VERSION} AS iii-image

FROM node:22-slim

ARG AGENTMEMORY_VERSION=0.9.28
ARG AGENTMEMORY_VERSION=0.9.29
ARG III_VERSION=0.11.2
ARG III_SDK_VERSION=0.11.2

Expand Down
2 changes: 1 addition & 1 deletion deploy/coolify/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ services:
context: .
dockerfile: Dockerfile
args:
AGENTMEMORY_VERSION: "0.9.28"
AGENTMEMORY_VERSION: "0.9.29"
III_VERSION: "0.11.2"
III_SDK_VERSION: "0.11.2"
restart: unless-stopped
Expand Down
2 changes: 1 addition & 1 deletion deploy/fly/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ FROM iiidev/iii:${III_VERSION} AS iii-image

FROM node:22-slim

ARG AGENTMEMORY_VERSION=0.9.28
ARG AGENTMEMORY_VERSION=0.9.29
ARG III_VERSION=0.11.2
ARG III_SDK_VERSION=0.11.2

Expand Down
2 changes: 1 addition & 1 deletion deploy/railway/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ FROM iiidev/iii:${III_VERSION} AS iii-image

FROM node:22-slim

ARG AGENTMEMORY_VERSION=0.9.28
ARG AGENTMEMORY_VERSION=0.9.29
ARG III_VERSION=0.11.2
ARG III_SDK_VERSION=0.11.2

Expand Down
2 changes: 1 addition & 1 deletion deploy/render/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ FROM iiidev/iii:${III_VERSION} AS iii-image

FROM node:22-slim

ARG AGENTMEMORY_VERSION=0.9.28
ARG AGENTMEMORY_VERSION=0.9.29
ARG III_VERSION=0.11.2
ARG III_SDK_VERSION=0.11.2

Expand Down
2 changes: 1 addition & 1 deletion deploy/render/render.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ services:
- key: PORT
value: "3111"
- key: AGENTMEMORY_VERSION
value: "0.9.28"
value: "0.9.29"
- key: III_VERSION
value: "0.11.2"
- key: III_SDK_VERSION
Expand Down
36 changes: 33 additions & 3 deletions integrations/filesystem-watcher/watcher.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
import { watch, promises as fsp, statSync } from "node:fs";
import { resolve, relative, join, extname, sep, basename } from "node:path";
import { randomBytes } from "node:crypto";
import { execFileSync } from "node:child_process";

// Same resolution order as the hooks' resolveProject (git toplevel basename,
// then directory basename) so a watched subdirectory scopes to the repository
// name instead of the subdirectory name.
function deriveProjectName(dir) {
try {
const top = execFileSync("git", ["rev-parse", "--show-toplevel"], {
cwd: dir,
stdio: ["ignore", "pipe", "ignore"],
encoding: "utf8",
}).trim();
if (top) return basename(top);
} catch {
// not a git repo
}
return basename(dir);
}

const TEXT_EXTENSIONS = new Set([
".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs",
Expand Down Expand Up @@ -123,7 +141,13 @@ export class FilesystemWatcher {
this.secret = config.secret;
this.project =
config.project ||
(this.roots[0] ? basename(this.roots[0]) : "filesystem-watcher");
(this.roots[0] ? deriveProjectName(this.roots[0]) : "filesystem-watcher");
Comment thread
coderabbitai[bot] marked this conversation as resolved.
// Per-root scope: a multi-root watcher must stamp each event with the
// project of the root that produced it, not the first root's project.
// An explicit config.project overrides for every root.
this.projectByRoot = new Map(
this.roots.map((r) => [r, config.project || deriveProjectName(r)]),
);
this.sessionId =
config.sessionId ||
`fs-watcher-${Date.now().toString(36)}-${randomBytes(3).toString("hex")}`;
Expand Down Expand Up @@ -214,7 +238,7 @@ export class FilesystemWatcher {
const payload = {
hookType: "post_tool_use",
sessionId: this.sessionId,
project: this.project,
project: this.projectByRoot.get(rootDir) ?? this.project,
cwd: rootDir,
timestamp: new Date().toISOString(),
data: {
Expand Down Expand Up @@ -319,7 +343,13 @@ export function configFromEnv(env = process.env) {
roots,
baseUrl: env.AGENTMEMORY_URL,
secret: env.AGENTMEMORY_SECRET,
project: env.AGENTMEMORY_PROJECT || null,
// AGENTMEMORY_PROJECT_NAME is the canonical override (matches the hooks);
// AGENTMEMORY_PROJECT stays as a deprecated alias for existing setups.
// Trimmed, with whitespace-only treated as unset, same as resolveProject.
project:
(env.AGENTMEMORY_PROJECT_NAME || "").trim() ||
(env.AGENTMEMORY_PROJECT || "").trim() ||
null,
sessionId: env.AGENTMEMORY_SESSION_ID || null,
ignorePatterns: extraIgnore,
allowBinary: env.AGENTMEMORY_FS_WATCH_ALLOW_BINARY === "1",
Expand Down
Loading