mirror of
https://github.com/rohitg00/agentmemory.git
synced 2026-09-14 20:16:33 +08:00
fix/engine-spawn-absolute-paths
485 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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
|
||
|
|
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).v0.8.10 |
||
|
|
9cef253e20 | Update README.md | ||
|
|
3452c9b3ab |
chore: viral gist callout + crop banner whitespace (#142)
* chore: add viral gist callout + crop banner whitespace - README: new sub-line under the tagline crediting the design doc gist at 669 stars / 91 forks, with a link. The gist extends Karpathy's LLM Wiki pattern with confidence scoring, lifecycle, knowledge graphs, and hybrid search — it's the spec agentmemory implements, so readers get context on what they're looking at. - assets/banner.png: cropped from 930x512 to 930x332 (removed ~70px dead space from the top and ~110px from the bottom). The wordmark, tagline, orange underline, and top of the neural network graphic are all intact. Aspect ratio is now ~2.8:1 which renders better in the GitHub repo header. * readme: bold viral-gist callout — shields.io badge in banner-orange Previous sub-line was buried as gray <sub>, practically invisible next to the npm/tests/license badges. Replace with a for-the-badge style shield in #FF6B35 (matches the orange underline in the banner) so it catches the eye the moment the page loads. Caption stays as <sub> underneath for people who want the context. |
||
|
|
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).v0.8.9 |
||
|
|
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 casesv0.8.8 |
||
|
|
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].v0.8.7 |
||
|
|
bfd1255b4f | Update README.md | ||
|
|
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 movev0.8.6 |
||
|
|
0166149dda | fix(retention): address CodeRabbit review (2) | ||
|
|
3935aecdae | chore: bump version to 0.8.5 + add CHANGELOG (#133) v0.8.5 | ||
|
|
a4f1676b23 | fix(retention): address CodeRabbit review feedback on eviction audits and legacy source handling | ||
|
|
3445314c50 | fix: route semantic memory eviction to correct KV store using source tracking | ||
|
|
c1a63b08d7 |
Merge pull request #131 from rohitg00/fix/mcp-notification-no-response-129
fix(mcp): never respond to JSON-RPC notifications (#129) |
||
|
|
461ef827fa |
mcp: validate request id type per JSON-RPC 2.0 §4
CodeRabbit flagged that processLine only checked `id != null` in the
invalid-shape path, which meant (a) malformed messages with non-primitive
ids could be echoed back with that id, and (b) valid-shape requests with
id: {} / [] / true would fall through to the handler and be responded to
with the bogus id as the response id — both spec violations.
Add isValidId() (string | number | null | undefined), extract rawId once
as unknown, and:
- In the invalid-shape path, only echo if rawId is string or number.
- After shape validation, reject bad-id requests with -32600 / id: null
before invoking the handler.
Adds 5 test cases covering id: object, array, boolean, plus the malformed
+ non-primitive-id drop path and a positive string-id case.
|
||
|
|
745ad87095 |
fix(mcp): never respond to JSON-RPC notifications (#129)
Codex CLI v0.120.0 fails to start the standalone MCP server with "MCP startup failed: Transport closed". Other MCP clients (Claude Desktop, Cursor, OpenClaw) work fine with the same binary. Reported in #129 by @GuillaumeOuint. Root cause: src/mcp/transport.ts unconditionally writes a JSON-RPC response for every incoming message, including notifications. Per JSON-RPC 2.0 §4.1 a notification is "a Request object without an id member" and "the Server MUST NOT reply to a Notification". Strict clients close the transport when they receive an unsolicited response — exactly the symptom Guillaume reported. The trigger sequence on Codex CLI: 1. Codex sends initialize (request, has id) → server responds ✓ 2. Codex sends notifications/initialized (notification, no id) 3. Our handler in standalone.ts returns {} 4. Transport writes {"jsonrpc":"2.0","result":{}} to stdout (no id field because request.id was undefined) 5. Codex parses an unsolicited response → closes transport 6. Guillaume sees "Transport closed" This worked accidentally for lenient clients (Claude Desktop, Cursor, etc.) which silently ignored the spurious responses. Fix: - transport.ts: detect notifications via missing/null id field, run the handler for side effects, then drop the response. Errors inside notification handlers are logged to stderr (not silently swallowed) so production debugging still works - transport.ts: malformed messages that have no id are also dropped silently (consistent with notification semantics) instead of being responded to with id: null - transport.ts: extracted processLine() so the line-handling logic is testable independently of process.stdin/stdout - new test/mcp-transport.test.ts with 9 tests covering: request responses (success + error), notification suppression (no id + null id + handler-throws), malformed input (parse error, invalid request shape with id, invalid request shape without id, empty lines) Tests: 693/693 (684 baseline + 9 new). Build clean. |
||
|
|
9bee06760a |
Merge pull request #130 from rohitg00/fix/windows-cli-followups-128
fix(cli): capture signal kills + guard empty env paths (#128 followups) |
||
|
|
397a0857e9 |
fix(cli): capture signal kills + guard empty env paths (PR #128 followups)
CodeRabbit flagged two remaining minor issues on the Windows diagnostics PR #128 that landed before they were addressed: 1. src/cli.ts:161 — the exit handler only treated non-zero numeric codes as abnormal. Signal-killed processes have code === null and signal !== null (e.g., SIGSEGV, SIGABRT, SIGKILL). Those crashes were silently dropped from startupFailure so the user got the generic "did not become ready" timeout instead of a real diagnosis. On Windows this rarely fires (abnormal exits usually carry codes) but on Unix a segfault in iii-engine would be invisible. Fix: widen the guard to `(code !== null && code !== 0) || (code === null && signal !== null)` so signal-only exits are captured too. Also generate a clearer fallback message when stderr is empty: "process killed by signal SIGSEGV" rather than the old "process exited with code null (SIGSEGV)". 2. src/cli.ts:114 — when USERPROFILE (Windows) or HOME (Unix) is unset, `join("", ".local", "bin", "iii.exe")` produces a relative path like `.local/bin/iii.exe`. Harmless today (the path is checked via existsSync which fails silently), but theoretically it could match an unintended file in the user's current working directory. Fix: guard the env var first, return an empty list (Windows) or skip the per-user path (Unix) when the env var is missing. Keep the absolute /usr/local/bin/iii fallback on Unix since it's valid regardless of HOME. No test changes — whichBinary and fallbackIiiPaths are CLI side-effect paths not covered by unit tests. Tests: 684/684. Build clean. |
||
|
|
a477f46536 |
Merge pull request #128 from rohitg00/fix/windows-startup-diagnostics-127
fix(cli): surface real engine startup errors, Windows support (#127) |
||
|
|
e2ee04ffa0 |
fix(cli): normalize whichBinary output for Windows CRLF (PR #128 review)
CodeRabbit caught: `execFileSync(...).trim().split("\n")[0]` leaves a
trailing \r on the first path whenever `where` on Windows returns
multiple matches. `.trim()` only strips leading/trailing whitespace —
the CRLFs between the matches stay, so split("\n")[0] yields
"C:\path\one.exe\r" which spawn() rejects as an invalid path.
Single-match outputs worked by accident because .trim() ate the final
\r\n before the split. The bug only fired when a user had iii installed
in two places simultaneously (e.g. a WSL install alongside a native
Windows install), which is a plausible but non-default setup.
Fix: split on /\r?\n/, trim each line, find the first non-empty one.
Also now returns `null` on empty output instead of the empty string
(behavior is identical through the caller's truthy check, but it
matches the documented Promise<string | null> contract exactly).
|
||
|
|
ffca536990 |
fix(cli): correct iii install commands — there is no cargo install iii-engine
Caught during PR #128 review: the install hints I added showed `cargo install iii-engine` as the recommended path on both Windows and Unix. That crate does not exist on crates.io. Verified via: - https://crates.io/api/v1/crates?q=iii (no iii-engine result) - https://crates.io/api/v1/crates/iii-engine (404) The actual install methods per github.com/iii-hq/iii releases: - macOS / Linux: curl -fsSL https://install.iii.dev/iii/main/install.sh | sh (downloads the prebuilt binary into ~/.local/bin/iii) - Windows: download iii-x86_64-pc-windows-msvc.zip from the GitHub release page, extract iii.exe, add to PATH or place at %USERPROFILE%\.local\bin\iii.exe (which our fallback already checks) - Docker: docker pull iiidev/iii:latest (already used by docker-compose) There is no PowerShell installer (https://install.iii.dev/iii/main/install.ps1 returns 404) and no scoop / winget / cargo path on Windows. The previous README "From source" line (`cargo install iii-engine`) was already wrong on main — fixed at the same time so we have one correct source of truth. Changes: - src/cli.ts installInstructions(): rewrite both Windows and Unix branches to show the correct upstream commands. Add link to https://iii.dev/docs at the bottom of each - src/cli.ts troubleshooting note: "iii-engine version mismatch" line no longer references cargo - src/cli.ts fallbackIiiPaths(): drop the .cargo/bin/iii.exe fallbacks on both platforms — they were dead paths since the crate doesn't exist. Keep .local/bin/iii.exe (matches where the sh installer puts it on Unix and where the README tells Windows users to drop iii.exe) - README.md "Windows" section: rewrite Option A as "download the prebuilt MSVC zip from GitHub releases", add explicit disclaimer note that there is no cargo install iii-engine path - README.md "From source" line: replace stale cargo command with the correct sh-installer / Windows-zip / Docker matrix Same test count (684 / 684), build clean. |
||
|
|
4beb35ab9e |
fix(cli): surface real engine startup errors, Windows support (#127)
Root cause: when the iii-engine binary crashes on startup or isn't
installed at all, the CLI spawned with `stdio: "ignore"` and swallowed
stderr entirely. 15 seconds later it reported "Check that ports 3111,
3112, 49134 are available" — which pointed users at the wrong problem.
On Windows this was the most common failure mode because most users
don't have Rust/cargo installed and nothing tells them they need it.
Changes:
- Capture engine stderr via `stdio: ["ignore", "ignore", "pipe"]` with
a 16KB cap, surface it on startup failure or in --verbose mode.
Processes still spawn detached/unref'd so the parent can exit cleanly.
- `windowsHide: true` so spawning a console app doesn't pop a window.
- Platform-aware iii binary fallback paths: on Windows, search
%USERPROFILE%\.cargo\bin\iii.exe, %LOCALAPPDATA%\cargo\bin\iii.exe,
and %USERPROFILE%\.local\bin\iii.exe. On Unix, add ~/.cargo/bin/iii
alongside the existing paths.
- Use `path.delimiter` instead of hardcoded `:` when prepending to PATH.
- Three-way diagnostic when the engine never becomes ready:
1. Process crashed → show the captured stderr + common causes
2. Process never started → platform-specific install instructions
3. Running but port not responding → platform-specific port check
command (netstat on Windows, lsof/ss on Unix)
- New `--verbose` / `-v` flag echoes spawn commands and surfaces engine
stderr even on success. Useful for diagnosing #127-style reports
across any platform.
- Docker compose lookup searches three candidate paths (was two), fixing
a latent bug where npm-installed docker-compose.yml at the package
root wasn't always found.
- README: new `### Windows` section in the install area documenting
Option A (rustup + cargo install iii-engine), Option B (Docker
Desktop), and Option C (standalone MCP only, no engine), plus a
symptoms→fix troubleshooting table.
No test changes needed — engine startup is a side-effect path exercised
only via the CLI binary itself, not imported by any unit test. Verified
via `npm test` (684/684) and `npm run build` (clean).
|
||
|
|
3bac0855d6 |
Merge pull request #126 from rohitg00/chore/release-0.8.4
chore: release 0.8.4 |
||
|
|
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. |
||
|
|
61bdcf2494 |
Merge pull request #121 from JasonLandbridge/fix/standalone-mcp-npx-command
fix: correct standalone MCP invocation to use --package flag |
||
|
|
2665981341 |
Merge remote-tracking branch 'origin/main' into fix/standalone-mcp-npx-command
# Conflicts: # README.md # integrations/hermes/README.md # integrations/openclaw/README.md # src/cli.ts |
||
|
|
2a5cb123dc |
fix: persist memories to disk immediately after memory_save (#122)
* fix: persist memories to disk immediately after memory_save
Previously, kv.persist() was only called on SIGINT/SIGTERM. If the
process was killed with SIGKILL, crashed, or the host terminated it
forcefully (e.g. OpenCode session end), all saved memories were lost.
Fix: call kv.persist() immediately after kv.set() in the memory_save
handler so data is written to standalone.json on every save.
Also export handleToolCall with an injectable kv parameter to allow
unit testing, and add tests verifying that persist is called on save.
* refactor: use generateId() and single isoNow in memory_save handler
Replace ad-hoc ID/timestamp logic with project-standard helpers:
- generateId("mem") instead of manual Date.now()/Math.random() concatenation
- Single const isoNow = new Date().toISOString() shared by createdAt and updatedAt
fingerprintId is intentionally not used: memory_save always creates a new
unique entry (no dedup), so a content-addressable key would silently overwrite
duplicate content rather than append — matching existing project behaviour.
|
||
|
|
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 |
||
|
|
54c32c71ac | fix: correct standalone MCP help text to use --package flag | ||
|
|
e83bb112ff |
fix: correct OpenCode MCP config format and add dedicated example
OpenCode uses a different MCP config format than other tools: - Config file is opencode.json (not .opencode/config.json) - Uses "type": "local" with "command" array (not mcpServers format) - Does not use separate "command"/"args" fields Also added an OpenCode-specific config block in the Standalone MCP section alongside the generic JSON example so it's easy to find. |
||
|
|
401666a8b6 |
fix: correct standalone MCP invocation to use --package flag
npx agentmemory-mcp fails because there is no standalone npm package named 'agentmemory-mcp'. The binary is registered inside the @agentmemory/agentmemory package and must be invoked with: npx --package @agentmemory/agentmemory agentmemory-mcp Updated all documentation examples across README.md and integration guides (openclaw, hermes) to use the correct invocation. This fixes MCP clients that followed the docs and got an npm 404 error. |
||
|
|
266583ca3a |
docs: add v0.8.2 security advisory drafts
Six advisory drafts, one per vulnerability fixed in v0.8.2. These match the content that should be published via GitHub's Security Advisories UI at: https://github.com/rohitg00/agentmemory/security/advisories/new Each advisory includes: - Severity + CVSS 3.1 vector - CWE classification - Affected/patched version ranges - Full summary of the issue - Impact analysis - Patch description with commit reference - Workarounds for users on affected versions - References and credit These drafts are reference material — the actual CVEs need to be filed through the GitHub UI to get GHSA IDs assigned and to appear in GitHub's vulnerability database for downstream scanners. Order: 01. viewer-xss (CRITICAL, 9.6) 02. curl-sh-rce (CRITICAL, 9.8) 03. default-bind-0000 (HIGH, 8.1) 04. mesh-unauth (HIGH, 7.4) 05. obsidian-export-traversal (MEDIUM, 6.5) 06. privacy-redaction-incomplete (MEDIUM, 6.2) |
||
|
|
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 updatedv0.8.2 |
||
|
|
649e6e44c5 |
feat: light-mode SVG variants for GitHub dark theme compatibility (#115)
Adds a full set of light-background SVG tags in assets/tags/light/
that mirror the existing dark-background tags in assets/tags/.
GitHub <picture> element auto-swaps based on the reader's theme:
- GitHub LIGHT theme → dark-bg SVG (original, stands out on white)
- GitHub DARK theme → light-bg SVG (new, stands out on dark)
## 30 light variants generated
Same dimensions, same orange #FF6B35 brand accent, same fonts. Only
the backgrounds and text colors change:
- Background: white → light gray gradient (#FFFFFF → #F8F9FB)
- Border: #E5E7EB (GitHub-style neutral border)
- Title text: #0F172A (slate-900)
- Subtitle text: #64748B (slate-500)
Stat values are slightly darkened for better contrast on white:
- Green: #00D26A → #16A34A
- Orange: #FF6B35 → #EA580C
- Purple: #B5A4FF → #7C3AED
## README changes
All 26 tag references now use <picture> elements:
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/tags/light/foo.svg">
<img src="assets/tags/foo.svg" alt="..." height="..." />
</picture>
Covers: 14 section headers (both stat rows × 6 = 12 stat cards).
No visual changes for light-theme readers — the original dark SVGs
still show. Dark-theme readers now get proper contrast instead of
dark-on-dark blend.
All 30 light SVGs validated with xmllint. 654/654 tests passing.
|
||
|
|
44b8f21e8a | fix: handle null result in agent-sdk (CodeRabbit review) | ||
|
|
59f7611a23 | fix(memory): address multimodal review feedback (bugs, perf, cleanup) | ||
|
|
b317433211 |
Adjust image heights in README statistics section
Updated image heights for various statistics in README. |
||
|
|
79a7a8ec04 |
feat: real agent logos + paste-this-prompt blocks for integrations (#114)
* feat: flashy README with real agent logos + paste-this-prompt blocks for integrations ## Main README — real logos in Supported Agents grid Replaced plain text cells with real brand logos for all 16 supported agents: - simple-icons CDN for: Claude Code (Anthropic), Cursor, Gemini CLI (Google Gemini), Codex CLI (OpenAI), Windsurf, Claude Desktop, Claude SDK - GitHub org avatars for: OpenClaw, OpenCode (opencode-ai), Cline, Hermes (NousResearch), Goose (block), Kilo Code (kilo-org), Aider (Aider-AI), Roo Code (RooCodeInc) - Shield badge for the generic "Any agent" cell Each logo is clickable and links to the upstream project. The OpenClaw and Hermes cells now link directly to integrations/openclaw and integrations/hermes so users can find the deeper plugin integration. Added tagline footer below the grid reinforcing the cross-agent story. ## Integration READMEs — paste-this-prompt blocks Following the gstack pattern, both integration READMEs now open with a visual hero (upstream logo + agentmemory logo) and a copy-pasteable text block the user can drop into their agent. The agent handles the entire setup — start server, update MCP config, verify health, open viewer. This matches the "paste this into Claude Code" block we already have in the main README for Claude Code users. ### openclaw/README.md - Centered hero: OpenClaw org avatar + agentmemory icon - Shield badges: 43 MCP tools, 4 lifecycle hooks, 95.2% R@5, self-hosted - "Install it in 30 seconds" section with the paste-this prompt ### hermes/README.md - Centered hero: NousResearch avatar + agentmemory icon - Shield badges: 43 MCP tools, 6 lifecycle hooks, 95.2% R@5, self-hosted - "Install it in 30 seconds" section with the paste-this prompt All 654 tests still passing. * fix: reorder agents, replace broken logos, add OpenClaw/Hermes paste blocks, cleaner hero ## Main README ### Supported Agents grid — 3 fixes 1. **Reorder**: OpenClaw and Hermes now in positions 2 and 3 (right after Claude Code), since they have first-class gateway plugins in integrations/. Users shouldn't have to scroll to find them. 2. **Fix broken Codex logo**: simple-icons CDN returned 404 for the OpenAI slug with black color. Switched ALL logos to GitHub org avatars (github.com/<org>.png?size=120) which are bulletproof and always return real images. Verified 7/7 resolve to actual PNGs. 3. **Bigger logos**: bumped from 40px to 48px for more visual weight. ### Paste-this-prompt toggles Added two collapsible <details> blocks after the Claude Code paste prompt: one for OpenClaw, one for Hermes. Each contains a complete copy-pasteable setup prompt that the agent can execute, plus a link to the full integration guide. ## Integration README heroes — redesigned The old hero had two logos separated by an ugly gray "+" shield badge that looked cheap. Replaced with: - agentmemory banner.png as the centered hero image - H1 title with a small inline upstream logo (28px) next to the title - Bolded tagline + subtitle with upstream link - Shield badge row (MCP tools, hooks, R@5, self-hosted, license) Much cleaner and follows the main README's visual pattern. Verified: 654/654 tests passing. * Update README.md * feat: custom SVG tags for section headers (no emojis) Replaces emoji section headers with custom SVG badges matching the agentmemory brand style (orange #FF6B35 accent on dark #1A1A1A). Inspired by shanraisshan/claude-code-best-practice's custom tag approach in its !/ directory. ## Custom SVG tags created (30 total in assets/tags/) ### 14 section headers (section-*.svg) Each is a 44px-tall dark rounded badge with: - Orange gradient accent strip on the left (brand color #FF6B35 → #FF8F5E) - Uppercase title in white, 800 weight, 2.2 letter-spacing - Light gray subtitle (#9CA3AF) with section context - Subtle #2A2A2A border on dark #1A1A1A → #0F0F0F gradient Sections: works-with-every-agent, benchmarks, vs-competitors, quick-start, why, how-it-works, search, mcp-server, real-time-viewer, configuration, api, architecture, development, license. ### 6 hero stat blocks (stat-*.svg) Big numbers with small uppercase labels, replacing the shield.io badge row in two places (hero + benchmarks section): - stat-recall: 95.2% R@5 (green) - stat-tokens: 92% fewer tokens (green) - stat-tools: 43 MCP tools (orange) - stat-hooks: 12 auto hooks (orange) - stat-deps: 0 external DBs (purple) - stat-tests: 654 tests passing (green) ### 8 pill tags (pill-*.svg) Rounded pills with colored dot indicator for inline status tags: MCP, AUTO HOOK, SKILL, NEW, BETA, STABLE, PLUGIN, SECURE. ### 1 "New in v0.8.2" version tag Dark pill with orange v0.8.2 version marker. ### 1 section divider Horizontal line with orange diamond in the middle (unused for now, available for future sections). ## README changes - All <h2> section headers now use <img> tags pointing to the SVG - Hero stats row replaced with stat-*.svg row (same visual position) - Benchmarks stats row replaced with the same stat-*.svg row - Nav links updated to match the new h2 anchor IDs - "New in v0.8.2" callout uses new-v082.svg tag + note - Cursor grid cell fixed (was missing <strong>Cursor</strong> label) - Zero emojis in the entire README All 30 SVGs validated with xmllint, 654/654 tests passing. * Remove version 0.8.2 image from README Removed image tag for version 0.8.2 from README. |
||
|
|
ae8f061cd6 |
feat: demo command, benchmark comparison, OpenClaw integration (#113)
* feat: add demo command, benchmark comparison, OpenClaw integration, token cost in viewer
Product & ecosystem improvements surfaced from the last30days research
and competitive analysis of the AI agent memory space.
## New
### agentmemory demo command
Seeds 3 realistic sessions (JWT auth, N+1 query fix, rate limiting)
and runs smart-search queries against them. Shows token-aware semantic
search finding "N+1 query fix" when you search "database performance
optimization" — proof that keyword matching can't do that.
npx @agentmemory/agentmemory demo
Surfaces the "show don't tell" moment in 30 seconds without requiring
the user to integrate anything.
### benchmark/COMPARISON.md
Head-to-head comparison vs mem0 (53K stars, 68.5% LoCoMo),
Letta/MemGPT (22K stars, 83.2% LoCoMo), Khoj (34K stars),
claude-mem (46K stars), and Hippo. Feature matrix across 18
dimensions. Token efficiency table. Honest caveats about LongMemEval
vs LoCoMo apples-vs-oranges. Lets users compare without trusting our
README.
### integrations/openclaw/
OpenClaw gateway plugin following the same pattern as the Hermes
integration. README, plugin.yaml with hooks config, and plugin.mjs
implementing the 4 lifecycle hooks (on_session_start, on_pre_llm_call,
on_post_tool_use, on_session_end). Gets agentmemory into the OpenClaw
ecosystem for distribution.
## Improved
### Viewer token savings card
Now shows tokens saved AND dollar cost saved (using $0.30/1K as a
reasonable mid-tier model rate) instead of just raw numbers. Makes
the ROI immediately tangible.
### README additions
- "Try it in 30 seconds" section featuring the new demo command
- Competitor comparison link in benchmarks section
- OpenClaw integration link in supported agents table
* refactor: simplify demo command and OpenClaw plugin
- Extract postJson helper in cli.ts runDemo (4x repeated fetch boilerplate → single call site)
- Break runDemo into buildDemoSessions, seedDemoSession, runDemoSearch for single-responsibility
- Replace three-branch if/else command dispatch with a commands map (single error handler)
- Extract typed DemoSession/DemoObservation/SearchResult interfaces
- Simplify OpenClaw plugin.mjs: collapse headers() + fetchJson() into a single postJson method
Each hook is now ~5 lines instead of ~10
No behavior change. 654/654 tests passing, CLI help and error paths verified.
* fix: address CodeRabbit review on growth changes
## plugin.mjs
- Honor the enabled config flag: constructor now sets this.enabled and
all four hooks (onSessionStart, onPreLlmCall, onPostToolUse,
onSessionEnd) return early when disabled
- Fail-fast on non-OK HTTP: previously !res.ok returned null regardless
of fallbackOnError. Now throws with status/statusText/body when
fallbackOnError is false
- Update header JSDoc to use the real hook names instead of the
fictional prefetch/capture/consolidate
## openclaw README.md
- Replace stale hook names (prefetch/capture/consolidate) with the
actual onSessionStart/onPreLlmCall/onPostToolUse/onSessionEnd names
matching plugin.mjs
- Add 'text' language identifier to the Project profile code fence to
satisfy markdownlint MD040
## cli.ts
- Replace ad-hoc Date.now()-based session IDs with generateId("demo")
from src/state/schema.ts, matching the rest of the codebase
(snapshot.ts, migrate.ts, consolidation-pipeline.ts)
- Add postJsonStrict helper that throws on non-OK with status + body
- seedDemoSession now uses postJsonStrict for session/start and
session/end (critical lifecycle calls that should fail fast)
- The observation POST in the loop now inlines the fetch to surface
HTTP errors via p.log.warn with status/body, still only incrementing
the stored counter on success
## viewer/index.html
- Fix token-to-dollar conversion that was 100x underreporting:
tokensSaved / 1000 * 0.3 returns DOLLARS, not cents. Now computes
dollars first, then cents = Math.round(dollars * 100)
- 100K tokens saved now correctly displays $30.00, not 30ct
All 654 tests still passing.
* fix: address 2 more CodeRabbit findings on plugin.mjs
- Use nullish coalescing (??) for config defaults so valid falsy
values like min_confidence: 0 or timeout_ms: 0 aren't overwritten
by defaults. || would clobber them.
- Fix /agentmemory/context payload to match the server contract at
src/triggers/api.ts:115. The handler expects { sessionId, project,
budget? } but the plugin was sending { sessionId, query, tokenBudget,
minConfidence }. Three issues:
1. Missing required 'project' field
2. Wrong field name: 'tokenBudget' should be 'budget'
3. 'query' and 'minConfidence' are not part of this endpoint's
contract (probably belong on /smart-search instead)
|
||
|
|
cbaaf4fb44 |
security: harden defaults, viewer CSP, mesh auth, export confinement (#108)
Fixes 6 security issues in agentmemory's default deployment, viewer, mesh sync, export path, and startup flow. ## Findings Addressed 1. **Default REST/stream exposure on 0.0.0.0** — bind default REST and stream services to 127.0.0.1; restrict Docker port publishing to localhost; add dedicated iii-config.docker.yaml for Docker usage. 2. **Stored XSS via inline viewer handlers** — remove inline event handlers from the viewer; replace with delegated data-action event handling; switch to nonce-based CSP for viewer HTML; add script-src-attr 'none'. 3. **Arbitrary path writes in Obsidian export** — confine exports to AGENTMEMORY_EXPORT_ROOT (default ~/.agentmemory); reject vaultDir values outside the allowed root. 4. **Mesh sync without peer auth** — require AGENTMEMORY_SECRET for mesh endpoints and sync operations; send Authorization: Bearer <secret> on mesh push/pull requests. 5. **Secret redaction misses modern token formats** — add coverage for Bearer tokens, sk-proj-*, broader sk/pk/rk/ak variants, GitHub ghs_/ghu_ tokens. 6. **CLI executes remote shell script directly** — remove curl|sh installation pattern; use existing local iii-engine binary if available, otherwise fall back to Docker. ## Validation - 654 tests passing (8 new security tests) - npm run build succeeds - Merged with main and resolved README conflicts to preserve the new benchmark/competitor table layout Thanks @eng-pf for catching these and submitting the fixes. |
||
|
|
dd17801adc |
Merge pull request #112 from rohitg00/feat/readme-redesign-benchmarks
feat: redesign README with benchmarks, competitor comparison, agent grid |
||
|
|
de068324ab |
feat: redesign README with benchmark tables, competitor comparison, and agent logos
- Add prominent benchmark table (LongMemEval 95.2% R@5 + token savings side-by-side) - Add competitor comparison table (agentmemory vs mem0 vs Letta vs built-in) - Add visual agent grid showing all 16 supported agents - Add stats row (95.2% R@5, 92% fewer tokens, 43 tools, 12 hooks, 0 deps, 646 tests) - Collapse verbose tool lists and API endpoints into expandable sections - Remove 34-row KV scopes table and 123-function breakdown (internal details) - Remove lengthy env var comments and inline code examples - Simplify "How It Works" with 4-tier consolidation table - Add token savings estimate to CLI status command - Reduce README from ~900 lines to ~540 lines while adding more visual impact |
||
|
|
3486288f46 | fix: address CodeRabbit review comments | ||
|
|
e692cf0095 | feat: implement multimodal image memory | ||
|
|
7af05ecf78 | chore: bump version to 0.8.1 v0.8.1 | ||
|
|
be24ef72ed |
fix: viewer not found when installed via npx (#109)
Build script now copies src/viewer/index.html to dist/viewer/. Also added more candidate paths for index.html resolution so the viewer works from dist/ (npx), src/ (tsx dev), and repo root. |
||
|
|
122efdeb44 |
docs: add all major agents to README, prioritized by popularity
Add OpenClaw (345K stars), Codex CLI (62K), Cline (59K), Aider (42K), Goose (33K), Kilo Code (1.5M users) to both Quick Start table and Supported Agents section. Ordered by GitHub stars/user count. |
||
|
|
e88de77f98 |
Merge pull request #107 from rohitg00/feat/hermes-integration
feat: Hermes Agent memory provider plugin |
||
|
|
9173b090f4 |
fix: address review findings on Hermes plugin
- SSRF prevention: validate URL scheme (http/https only) before urlopen - on_pre_compress: inject fetched context into messages list (was no-op) - Remove unused self._secret; _api reads from env directly |
||
|
|
112e6f4bc0 |
feat: add Hermes Agent memory provider plugin
Two integration paths: 1. MCP server (zero code) - add to ~/.hermes/config.yaml 2. Memory provider plugin (deeper) - hooks into Hermes agent loop Plugin implements the full MemoryProvider ABC: - prefetch() injects relevant memories before each LLM call - sync_turn() captures conversation turns in background (non-blocking) - on_session_end() marks sessions for summarization - on_pre_compress() re-injects context before compaction - on_memory_write() mirrors MEMORY.md writes - system_prompt_block() injects project profile - 3 tools: memory_recall, memory_save, memory_search Uses only stdlib (urllib, json, threading). No new dependencies. |