mirror of
https://github.com/rohitg00/agentmemory.git
synced 2026-09-14 20:16:33 +08:00
v0.9.20
353 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
08d781d431 |
chore(release): v0.9.20 — hotfix Codex Stop revert (#501)
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.v0.9.20 |
||
|
|
a953cadb62 |
Revert "fix: end Codex sessions on Stop hook (#495)" (#501)
This reverts commit
|
||
|
|
29e6ebdcc0 | docs(changelog): credit contributors on v0.9.19 entries | ||
|
|
bb259acedc |
chore(release): v0.9.19 — route around upstream iii-console installer bug
`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.
v0.9.19
|
||
|
|
caa9f5223e |
fix(plugin): inherit AGENTMEMORY_URL/SECRET via env passthrough (#375) (#460)
The bundled plugin .mcp.json + the `agentmemory connect <agent>` wiring
both hardcoded the MCP server to localhost:3111 with no env-var seam.
Users running agentmemory remotely (k8s cluster-internal DNS, reverse
proxy with API key auth) hit a permanent /doctor warning because they
had to add a second entry with the right AGENTMEMORY_URL + SECRET, and
both entries fought for the "agentmemory" name.
Both files now use ${AGENTMEMORY_URL} / ${AGENTMEMORY_SECRET}
expansion. Host (Claude Code, Cursor, etc.) substitutes the shell
value at MCP-server launch; when vars are unset the host passes empty
string, which the standalone shim treats as missing and falls back to
http://localhost:3111. One wired entry now covers both local and
remote without duplicates.
- plugin/.mcp.json: env block added with ${VAR} expansion
- src/cli/connect/util.ts: AGENTMEMORY_MCP_BLOCK env updated; comment
documents the empty-string fallback contract
- test/cli-connect.test.ts: regression test asserting the env block
shape after install() — protects against future regressions where
the literal hardcoded URL leaks back in
1008/1008 tests pass.
|
||
|
|
abeec1d0b7 |
fix: end Codex sessions on Stop hook (#495)
Signed-off-by: Nanami <Rex57@users.noreply.github.com> Co-authored-by: Nanami <Rex57@users.noreply.github.com> |
||
|
|
06248fd199 |
refactor(providers): extract OpenAI transport into shared module (#371) (#462)
* refactor(providers): extract OpenAI transport into shared module (#371) OpenAIProvider (LLM) and OpenAIEmbeddingProvider (embedding) both speak the OpenAI wire shape over POST /v1/chat/completions and POST /v1/embeddings — and both need to swap to the Azure shape when the base URL lands at *.openai.azure.com (api-key header instead of Authorization: Bearer, api-version query param, no /v1/ prefix because the deployment is already in the URL). Until now only the LLM provider knew about Azure. The embedding provider hardcoded /v1/embeddings + Bearer auth, so any user pointing OPENAI_BASE_URL at an Azure resource saw their LLM calls route correctly while embedding calls 404'd / 401'd. New src/providers/_openai-shared.ts owns: detectAzure(baseUrl) buildChatUrl(baseUrl, isAzure, apiVersion) buildEmbeddingUrl(baseUrl, isAzure, apiVersion) buildAuthHeaders(apiKey, isAzure) normalizeBaseUrl(raw) Both classes import these helpers. The embedding provider gains Azure auto-detection via the same OPENAI_BASE_URL hostname check the LLM provider already uses, plus the same OPENAI_API_VERSION knob (default 2024-08-01-preview). Closes the gap users hit when mirroring an Azure deployment on both surfaces. Net delta: ~40 LOC of duplicated transport collapsed into one shared file; both provider files are smaller and focused on their surface's unique concerns (LLM owns max_tokens / reasoning_effort / timeout precedence; embedding owns the MODEL_DIMENSIONS table). Approach diverges from the issue body (single class implementing both interfaces): the existing factory contracts at src/providers/index.ts dispatch LLM and embedding independently (detectProvider vs detectEmbeddingProvider), and merging into one class would force constructors to coordinate detection across two config surfaces. Shared transport keeps factories clean while delivering the DRY win and the Azure-on-embeddings parity gain. - src/providers/_openai-shared.ts: new shared transport module - src/providers/openai.ts: import shared helpers; drop local detectAzure + private buildUrl/buildHeaders - src/providers/embedding/openai.ts: import shared helpers; Azure auto-detection via OPENAI_BASE_URL host check, OPENAI_API_VERSION query param when Azure-shaped - test/openai-shared.test.ts: 17 cases covering all four helpers plus end-to-end embedding Azure shape (asserts URL path + api-key header substitution via mocked fetch) 1023/1023 tests pass. * fix(providers): use URL API for Azure path composition (review) Inline review on #462 surfaced an edge-case bug in the azureUrl helper: it tested baseUrl for an existing query string to pick the ?/& separator, then string-concatenated the route path BEFORE that separator. If a baseUrl arrived with pre-existing query params (corporate proxy carrying a tenant token, diagnostics endpoint, etc.), the route path got interpolated into the query string, producing a malformed URL like: baseUrl: https://proxy.example.com/openai/deployments/d?tenant=acme result: https://proxy.example.com/openai/deployments/d?tenant=acme/chat/completions&api-version=2024-08-01-preview ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in the query, not the path Switch to the URL API: parse baseUrl, normalise the existing pathname (strip trailing slashes), join the route path with a single boundary slash, set api-version via url.searchParams.set (handles encoding without a manual encodeURIComponent), then toString. Existing query params are preserved. Test header normalisation pass: the OpenAIEmbeddingProvider Azure-detection tests captured RequestInit.headers as Record<string,string>. buildAuthHeaders happens to return a plain record today, but a future contributor migrating to HeadersInit/Headers would have these tests silently pass-but-stale because Record-style access would no longer match. Wrap the capture in new Headers(init?.headers) so the assertions go through .get() and tolerate any of the three valid HeadersInit shapes. Skipped — replacing DEFAULT_AZURE_API_VERSION with /openai/v1: the v1 Azure pattern is a recent addition and many deployments still require the api-version query param. Switching the default would break those deployments. Separate opt-in flag (e.g. OPENAI_AZURE_API_STYLE=v1) is the right shape but out of scope for this minimal-change review pass. - src/providers/_openai-shared.ts: azureUrl rewritten on URL API - test/openai-shared.test.ts: two new cases (query-param preservation, trailing-slash normalisation); embedding Azure tests switched to Headers API + .get() / .toBeNull() 1025/1025 tests pass. * feat(providers): support Azure OpenAI v1 URL pattern (review) Azure shipped a v1 GA endpoint pattern that mirrors the OpenAI wire shape one-for-one: POST https://<resource>.openai.azure.com/openai/v1/chat/completions No api-version query param, no /deployments/<dep> path segment — deployment name is passed in the request body as `model`. Existing OpenAI clients work as drop-in. The legacy URL pattern (/openai/deployments/<dep>/chat/completions?api-version=<date>) is still supported by Azure indefinitely for back-compat. Verification of the inline review's "remove DEFAULT_AZURE_API_VERSION and migrate all Azure calls to /openai/v1" instruction: removing it flat would break legacy users mid-flight (their existing OPENAI_BASE_URL=https://r.openai.azure.com/openai/deployments/d configs would still route through the legacy path and 400 without api-version). Hybrid auto-detect off the URL shape is the right shape: - baseUrl pathname contains `/openai/deployments/` -> legacy style, api-version still appended (DEFAULT_AZURE_API_VERSION fallback) - otherwise (bare resource host, /openai prefix, /openai/v1 prefix) -> v1 style, no api-version, route through /openai/v1/<path> Users migrate by stripping the /openai/deployments/<dep> suffix from their OPENAI_BASE_URL — no env-var changes required. azureStyleOf(baseUrl) is the new detection helper. v1AzureUrl preserves any pre-existing query params on the base URL (corporate proxy diagnostics tokens) but never appends api-version. The /openai/v1 prefix is normalised so configurations like OPENAI_BASE_URL=https://r.openai.azure.com/openai or .../openai/v1 don't double-prefix. - src/providers/_openai-shared.ts: azureStyleOf + v1AzureUrl; legacyAzureUrl (renamed from azureUrl) is unchanged; module header comment documents both styles and the migration path - test/openai-shared.test.ts: 3 new cases — v1 routing on bare host, /openai or /openai/v1 prefix normalisation, v1 on the embedding surface as well; legacy assertion updated to say "Azure legacy" so the contrast with v1 is explicit 1028/1028 tests pass. |
||
|
|
249d8ff032 |
feat(commits): link agent sessions to git commits (#498)
* feat(commits): link agent sessions to git commits
Adds a commit-link layer so memories captured during an agent session
can be traced back to the git commit they produced, and vice versa.
What ships:
- KV.commits namespace storing CommitLink records keyed by full SHA.
- Session.commitShas[] back-reference for fast forward lookup.
- POST /agentmemory/session/commit upserts a link (sha, branch, repo,
message, author, authoredAt, files, sessionId); merges sessionIds on
re-link and preserves linkedAt.
- GET /agentmemory/session/by-commit?sha= hydrates the link plus its
sessions.
- GET /agentmemory/commits?branch=&repo=&limit= lists recent links,
filtered and sorted desc by linkedAt, limit clamp 1..500 (default 100).
- src/hooks/post-commit.ts shells git rev-parse/log/diff-tree and POSTs
to /agentmemory/session/commit. Honors AGENTMEMORY_URL,
AGENTMEMORY_SECRET, AGENTMEMORY_CWD, AGENTMEMORY_SESSION_ID,
AGENTMEMORY_COMMIT_SHA. Best-effort, 1500 ms timeout. Wired into
tsdown hookEntries so it bundles to dist/hooks/ and plugin/scripts/.
- Two MCP tools: memory_commit_lookup, memory_commits.
- Four user-invocable plugin skills:
- commit-context: traces a file/function/line via git blame plus
memory_commit_lookup plus memory_recall.
- commit-history: lists agent-linked commits; parses branch=/repo=/
limit= from $ARGUMENTS.
- handoff: resumes the most recent session for cwd; surfaces an
unanswered user-facing question before the brief.
- recap: summarizes the last N sessions for cwd, grouped by date;
parses last <n> / today / this week.
Wire-up to capture from a real repo:
ln -sf "$(realpath node_modules/agentmemory/dist/hooks/post-commit.mjs)" .git/hooks/post-commit
chmod +x .git/hooks/post-commit
No new dependencies. Additive only.
* fix(commits): address CI + review findings on commit-link feature
- Update mcp-standalone.test.ts to expect 14 CORE_TOOLS (was 12) now that
memory_commit_lookup and memory_commits are registered. Restores CI on
Node 20 and 22.
- Wrap CommitLink upsert and session.commitShas mutation in withKeyedLock
to prevent lost updates when concurrent post-commit hooks race against
each other or against api::session::commit callers. Locks keyed by
`commit:<sha>` and `session:<sessionId>` so unrelated writers do not
serialize.
- Parallelize hydration of linked sessions via Promise.all in both
api::session::by-commit and the memory_commit_lookup MCP handler;
serial kv.get loops dragged response time for commits with many
sessions.
- Sharpen handoff SKILL.md project-path matching: resolve relative
$ARGUMENTS to an absolute normalized path and require directory-
boundary equality, not raw string prefix, so sibling repos sharing a
prefix do not collide.
- Sharpen commit-history SKILL.md HTTP fallback: build the
/agentmemory/commits URL with URL-encoded query values so branch and
repo values containing `?`, `&`, or `#` cannot corrupt the request.
* chore(consistency): bump documented tool and endpoint counts
Bring README.md, AGENTS.md, and the boot log in src/index.ts in line
with the live tool registry (53 MCP tools, +2 from this branch) and
the live REST registration (124 endpoints, +3 from this branch).
The Consistency test in test/consistency.test.ts derives both counts
from source and asserts the markdown matches; this commit only updates
the literal numbers, no behavior change.
|
||
|
|
4db303b6b9 |
perf(graph): swap BFS for Dijkstra over edge-weighted graph (#328) (#463)
* perf(graph): swap BFS for Dijkstra over edge-weighted graph (#328) Memory graph edges carry weights from 0.1 to 1.0 (encoding relation strength). BFS visits nodes in edge-count order regardless of weight, so a one-hop weak edge ranked the same as a two-hop chain of strong edges to the same node — which is the wrong semantics for the weighted graph the retrieval surface actually queries. Replace bfsTraversal with dijkstraTraversal: cost(edge) = 1 / max(edge.weight, 0.01) Higher-weight edges cost less, so Dijkstra prefers strong-edge paths. The clamp floor at 0.01 protects against malformed edges with weight=0 dividing by zero (the documented weight floor is 0.1, but imports / older snapshots may carry zeros). Same swap tightens the perf profile: - Adjacency built once in O(V+E) (was: filter allEdges per visited node, O(V·E) overall). - Min-heap dequeue is O(log V) per pop (was: Array.shift() which is O(n) — the dominant cost on graphs above ~200 nodes per the contributor's benchmark linked in #328). Min-heap is a 50-LOC inline class so the file doesn't take a new dependency for the perf-critical inner loop. Semantics preserved: - maxDepth still bounds edge-count, not accumulated cost. So a deep chain of strong edges still terminates at maxDepth hops. - One path per reachable node within depth (matches what the existing scoring loop consumes). - score = avgWeight × (1/pathLength) formula unchanged. - src/functions/graph-retrieval.ts: dijkstraTraversal + MinHeap - test/graph-retrieval.test.ts: 4 new cases — weight-optimal path selection (the core Dijkstra promise), disconnected-node safety, near-zero-weight clamp, maxDepth bound preserved 1011/1011 tests pass. * fix(graph): exclude startNode from Dijkstra returned paths (review) Inline review on #463 surfaced a pre-existing bug carried forward through the BFS-to-Dijkstra refactor: the returned path list included the startNode's own length-1 path, so the generic path-scoring loop in searchByEntities ran first — pathLength=1 with no edges → avgWeight fell to 0.5 (empty-edgeWeights fallback) → score=0.5 → startNode obs marked visited. The dedicated score=1.0 fallback loop for startNode obs was then skipped by the visitedObs guard, making it dead code. So startNode observations were silently scoring 0.5 instead of the intended 1.0. Drop the startNode entry from pathTo before returning. The caller's fallback loop now fires as designed. - src/functions/graph-retrieval.ts: pathTo.delete(startNode.id) immediately before Array.from(pathTo.values()); inline comment explains why the fallback would otherwise be dead. - test/graph-retrieval.test.ts: regression asserts obs_root carries score=1.0 and pathLength=0 when its node is the matched start. 1012/1012 tests pass. |
||
|
|
9061da56d5 |
chore(release): v0.9.18 (#459)
Hardening + DX wave. Bundles the four follow-up issues filed during the v0.9.17 audit (#446 timeout env unify, #447 viewer CSP tighten, #448 fs-watcher PEM/JWT redaction, #449 mcp-standalone probe DI flake) plus the lessons-auto-inject gap surfaced in discussion #381 / fixed in #457. 1007/1007 tests pass. Files bumped: - package.json - packages/mcp/package.json - plugin/.claude-plugin/plugin.json - plugin/.codex-plugin/plugin.json - src/version.ts - src/types.ts (ExportData.version union) - src/functions/export-import.ts (supportedVersions Set) - test/export-import.test.ts (assertion) - CHANGELOG.md (new section)v0.9.18 |
||
|
|
a557e34f39 |
fix(context): auto-inject lessons into mem::context payload (#457) (#458)
Lessons were generated (via mem::lesson-save / mem::reflect) and stored in the lessons KV scope, but never folded into the auto-inject payload the session-start hook prepends to the first turn. Agents only saw lessons if they explicitly called memory_lesson_recall — which they rarely did unprompted. Half the loop: lessons learned, lessons never reused. mem::context now reads lessons KV alongside slots + profile, filters out tombstoned entries + cross-project leakage, ranks project-scoped lessons above globals (1.5x boost) then by confidence, caps at 10, and emits a "## Lessons Learned" block sized like every other context block so the outer token-budget loop drops it cleanly when tight. Surfaced in #381 (@rockarxiv asked where lessons get used — answer was "nowhere, automatically"). Filed as #457, fixed here. - src/functions/context.ts: parallel-fetch lessons with slots + profile, render block after profile and before sessions - test/context-lessons.test.ts: 8 cases covering inclusion, empty state, project ranking, cross-project isolation, deleted-skip, top-N cap, confidence rendering, optional context-string append 1007/1007 tests pass. |
||
|
|
49377db0db |
fix(providers): unify OPENAI_TIMEOUT_MS + AGENTMEMORY_LLM_TIMEOUT_MS (#446) (#453)
* fix(providers): unify OPENAI_TIMEOUT_MS + AGENTMEMORY_LLM_TIMEOUT_MS (#446) v0.9.17 shipped OPENAI_TIMEOUT_MS scoped to the OpenAI LLM provider (inline AbortController, default 60s). PR #379 then shipped AGENTMEMORY_LLM_TIMEOUT_MS in the shared src/providers/_fetch.ts helper used by every other raw-fetch provider (Gemini, OpenRouter, MiniMax, OpenAI/Cohere/Voyage/OpenRouter embedding). Two env vars, same value, different names — ops confusion. Unify on the global name while keeping back-compat: 1. OPENAI_TIMEOUT_MS — OpenAI-scoped alias, takes precedence 2. AGENTMEMORY_LLM_TIMEOUT_MS — global fall-back across providers 3. 60 000 ms default The OpenAI LLM provider now routes through the shared fetchWithTimeout helper, dropping ~30 lines of duplicate AbortController + clearTimeout plumbing. Existing users with OPENAI_TIMEOUT_MS set keep the exact v0.9.17 behaviour; new users setting AGENTMEMORY_LLM_TIMEOUT_MS get the OpenAI LLM path covered too. README + .env.example now document AGENTMEMORY_LLM_TIMEOUT_MS as the canonical name and note OPENAI_TIMEOUT_MS as the OpenAI-scoped alias. 4 new precedence tests in test/fetch-timeout.test.ts cover all four env-var combinations. * test(providers): strict parse for OPENAI_TIMEOUT_MS env (CodeRabbit) CodeRabbit caught parseInt("30ms", 10) silently returning 30 in the timeout-resolve path. Real users hitting this would think they bound the call to 30ms when the regex would have rejected it. parsePositiveInt now rejects anything that isn't pure digits via /^\d+$/ (after trim). parseInt's lenience on trailing units / underscores / signs is gone — those fall back to the 60s default instead of masquerading as an aggressive bound. New regression test covers "30ms", "1_000", "60s", "30abc", "-30", "0". Whitespace padding (e.g. " 30 ") is still accepted — that's normal env-var handling. 992/992 tests pass on the worktree. |
||
|
|
00df540c87 |
fix(fs-watcher): redact PEM private-key blocks and standalone JWTs in previews (#448) (#450)
PR #332 covered KEY=value and Bearer-prefixed tokens. This extends the filesystem watcher's preview pipeline to also collapse multi-line and inline PEM private-key blocks (RSA / DSA / EC / OPENSSH / ENCRYPTED / generic) into a single REDACTED line bracketed by the BEGIN/END markers, and to redact standalone JWT-looking strings outside Bearer context. JWT redaction requires a three-segment dot pattern AND total length >= 100 chars to avoid matching incidental base64-shaped words. Closes #448 |
||
|
|
92e5c885fc |
fix(mcp): inject livez probe to kill mcp-standalone test flake (#449) (#451)
The MCP standalone shim's REST proxy probed `:3111/agentmemory/livez` via a hard-coded `fetch()` with a 2s AbortController timeout. In CI, vitest's mock setup raced the in-flight probe — tests randomly failed on call counts and response-shape assertions depending on whether the probe completed before each test's own deadline. This file has been the source of every "10-11 pre-existing failures unrelated to this PR" footnote for the last five releases. Add a `setLivezProbe(fn)` module-level seam in `src/mcp/rest-proxy.ts` (option A in the issue). Default stays the real fetch-based probe — wire behaviour is unchanged in production. `resetHandleForTests()` also resets the probe so tests can't accidentally leak a stub across files. In `test/mcp-standalone.test.ts`, install an instant `ok:false` probe in `beforeEach` so the shim always takes the deterministic InMemoryKV fallback path. Replace `globalThis.fetch` with a trap that throws if hit — any regression that bypasses the seam fails loudly instead of timing out silently and re-introducing the flake. Validation: 10/10 consecutive runs green (~185ms each, down from 2.2s); proxy test file unaffected (14/14); full suite 988/988 stable across 3 runs. |
||
|
|
57c785f4db |
fix(viewer): self-host SVG favicon + restore tight img-src CSP (#447) (#452)
#313 added `data:` to the viewer img-src so an inline-SVG favicon encoded as `data:image/svg+xml,...` could load. Bare `data:` allows arbitrary data-URI image types (data:image/png;base64,..., etc), which the viewer does not need. Self-host the favicon at `/favicon.svg` and revert the CSP to `img-src 'self'`. Same artwork (dark rounded tile + green "AM"), served from a real URL with `Content-Type: image/svg+xml` and `Cache-Control: public, max-age=3600`. - new file: src/viewer/favicon.svg (extracted from the URL-decoded inline blob) - src/auth.ts: img-src reverted to 'self' - src/viewer/index.html: favicon link now href="/favicon.svg" - src/viewer/server.ts: new GET /favicon.svg route, candidate-path loader mirrors loadViewerTemplate() so it resolves both from source (vitest) and from dist/ (npm run start) - package.json: build script copies favicon.svg into dist/viewer/ alongside index.html - test/viewer-security.test.ts: CSP assertion now requires bare `img-src 'self'` and rejects any `data:` allowance; new e2e test hits /favicon.svg and verifies SVG payload + headers |
||
|
|
c93c715043 |
fix(test): stub history + removeAttribute in viewer sandbox (#445)
The viewer-session-id sandbox (added in #366) ran fine until #313 landed hash-routing in `switchTab` — `updateTabRoute` now calls `history.replaceState`, and the aria-selected toggle calls `removeAttribute` on the non-active tab buttons. Neither was stubbed in the vm sandbox, so the combined main produced 2 failures: TypeError: b.removeAttribute is not a function ReferenceError: history is not defined Both PRs (#366, #313) passed CI in isolation. The combined failure only surfaced after merge. Stub both globals. 987/987 tests now pass on main. |
||
|
|
63a0742447 |
fix: stabilize viewer navigation and timeline layout (#313)
* fix: stabilize viewer navigation and timeline layout * fix: address viewer review nits |
||
|
|
e2f2217f59 |
fix: redact filesystem watcher previews before transport (#332)
* fix: redact filesystem watcher previews * fix: redact quoted sensitive preview keys --------- Co-authored-by: 이민재 <19909783+honor2030@users.noreply.github.com> |
||
|
|
e9f447c80e |
fix: recover stale sessions before eviction (#387)
* fix: recover stale sessions before eviction Constraint: preserve recoverable session insights during stale cleanup Confidence: high Scope-risk: narrow Tested: npm test -- test/evict.test.ts --reporter=dot; npx tsdown; git diff --check Tested: npm test -- --reporter=dot (fails on existing Windows path-sensitive compress-file and obsidian-export tests) Signed-off-by: laplace young <yangqk12@whu.edu.cn> * fix: refine stale session recovery guard Constraint: preserve raw-only stale sessions until a compression recovery path exists Confidence: high Scope-risk: narrow Tested: npm test -- test/evict.test.ts --reporter=dot; npx tsdown; git diff --check Signed-off-by: laplace young <yangqk12@whu.edu.cn> --------- Signed-off-by: laplace young <yangqk12@whu.edu.cn> Co-authored-by: laplace young <yangqk12@whu.edu.cn> |
||
|
|
0ee3e6510e |
fix(viewer): handle sessions missing ids (#366)
* fix(viewer): handle sessions missing ids * fix(viewer): address session id review feedback --------- Co-authored-by: honor2030 <19909783+honor2030@users.noreply.github.com> |
||
|
|
97ae69a3e4 |
fix: add AbortController timeout to all raw-fetch providers (#379)
* fix: add AbortController timeout to all raw-fetch providers (closes #373) Signed-off-by: Satyam Pratibhan <142714564+SATYAM-PRATIBHAN@users.noreply.github.com.> * fix: validate timeout ms and compose upstream AbortSignal in fetchWithTimeout Signed-off-by: Satyam Pratibhan <142714564+SATYAM-PRATIBHAN@users.noreply.github.com.> --------- Signed-off-by: Satyam Pratibhan <142714564+SATYAM-PRATIBHAN@users.noreply.github.com.> |
||
|
|
1c8713f978 | fix(hermes): is_available() must not do network calls (#377) | ||
|
|
57ffe91302 |
fix(hermes): use correct API keys tool_input/tool_output in sync_turn (#384)
The Hermes plugin's sync_turn() method sends conversation observations to the agentmemory /observe endpoint using keys 'input' and 'output', but the server's mem::observe handler extracts data using 'tool_input' and 'tool_output' (see src/functions/observe.ts). This key mismatch means raw.toolInput and raw.toolOutput are always undefined. When auto-compress is enabled, the LLM receives a nearly empty compression prompt (only timestamp + hookType + toolName), which produces generic, useless observations like: title: "Agent initiated conversation" narrative: "No specific content was provided" importance: 1 With the corrected keys, DeepSeek/Vision models receive the actual conversation content and produce meaningful, structured observations: title: "Verified fix for agentmemory plugin key naming" importance: 6 concepts: ["agentmemory plugin", "tool_input/tool_output key renaming"] facts: ["The fix replaces 'input'/'output' keys with..."] |
||
|
|
60a650d8c9 | fix(cli): authenticate apiFetch when AGENTMEMORY_SECRET is set (#380) | ||
|
|
d271fb41cb |
fix(coolify): use environment map syntax for Coolify v4 parser (#424)
Coolify v4's PHP-based docker-compose validator rejects the shorthand
environment list syntax ('- VAR' without =) with:
Error: non-string key in services.agentmemory.environment: 0
Switching to the map form with explicit variable substitution preserves
the original 'inherit from build env' behavior — Coolify still resolves
SERVICE_FQDN_AGENTMEMORY_3111 from the magic variable mechanism — while
satisfying the stricter parser.
Verified with:
SERVICE_FQDN_AGENTMEMORY_3111=https://example.com \
docker compose -f deploy/coolify/docker-compose.yml \
--project-directory deploy/coolify config
Tested live on a Coolify v4.0.0-beta.397 instance: deploy succeeds and
container reaches running:healthy on first try with this change.
|
||
|
|
3e7b61efd0 |
fix(security): reject non-loopback Host headers in viewer server (DNS rebinding) (#294)
The viewer (default :3113) binds to 127.0.0.1 and proxies every request to the local REST API with the AGENTMEMORY_SECRET attached as Bearer auth. The Origin allowlist defends against a plain cross-origin attacker page, but it does not defend against DNS rebinding: a victim visiting attacker.com whose authoritative DNS short-TTL-rebinds to 127.0.0.1 ends up issuing same-origin fetches to the viewer (origin and target both http://attacker.com:3113 from the browser's perspective), no preflight fires, and the proxy adds the secret server-side. The attacker page then reads every memory the user has stored. Fix: derive a Host-header allowlist from VIEWER_ALLOWED_ORIGINS plus the viewer's actual listen port (resolved post-listen so port=0 unit tests still work) and reject any other Host with 403 before the route logic runs. An explicit VIEWER_ALLOWED_HOSTS env var is honoured for reverse- proxy setups. Tests cover the predicate plus an end-to-end http.request that confirms an attacker-controlled Host header is rejected before the HTML / proxy paths. Detected by Aeon + manual review. Severity: high CWE-350 (Reliance on Reverse DNS Resolution for a Security-Critical Action) Co-authored-by: aeonframework <aeon@aaronjmars.com> |
||
|
|
3a3f866ab5 |
chore(release): v0.9.17 — OpenAI provider + telemetry id + Compare polish (#432)
Patch bump per the established rule: additive surface only. OpenAI provider is a new optional surface that activates only when OPENAI_API_KEY is set, gated by OPENAI_API_KEY_FOR_LLM. Telemetry project_name pin is pure observability metadata. Compare polish is docs/website only. PRs included since v0.9.16: #307 — OpenAI-compatible LLM provider (universal adapter for OpenAI, Azure OpenAI auto-detected by hostname, DeepSeek, SiliconFlow, vLLM, LM Studio, Ollama). Plus the maintainer- pushed Azure detection + fetch timeout + README scope hint follow-ups. Closes #185, #232, #312, supersedes #240. #426 — pin worker telemetry project_name #427 — Compare section polish (title + native plugins cell + grid) Files bumped (9): package.json, packages/mcp/package.json, plugin/.claude-plugin/plugin.json, plugin/.codex-plugin/plugin.json, src/version.ts, src/types.ts, src/functions/export-import.ts, test/export-import.test.ts, CHANGELOG.mdv0.9.17 |
||
|
|
3166780d2c |
feat: add OpenAI-compatible LLM provider (#307)
* feat: add OpenAI-compatible LLM provider - Add OpenAIProvider using raw fetch (no SDK dependency) - Supports any /v1/chat/completions endpoint: OpenAI, DeepSeek, SiliconFlow, Azure OpenAI, vLLM, LM Studio, Ollama - Auto-detects OPENAI_API_KEY with OPENAI_API_KEY_FOR_LLM opt-out - Add OPENAI_REASONING_EFFORT passthrough for thinking models (e.g. Ollama Cloud kimi-k2.6) to ensure content is populated - Update README with OpenAI provider table, env vars, and reasoning config * fix(config): honor OPENAI_API_KEY_FOR_LLM=false in detectLlmProviderKind `detectProvider()` correctly gates OpenAI auto-detection on OPENAI_API_KEY_FOR_LLM !== "false", but `detectLlmProviderKind()` did not — so users who set OPENAI_API_KEY only for embeddings (via the existing OPENAI_BASE_URL + OPENAI_EMBEDDING_MODEL flow from #186) would see /agentmemory/config/flags report `provider: llm` even though detectProvider() routed them to the noop provider. Also clarify in the README that OPENAI_REASONING_EFFORT is honored only by reasoning models (o1, o3, gpt-*-reasoning) and providers that mirror that schema (Ollama Cloud thinking models). Standard chat models reject the field with 400. Verified: - OPENAI_API_KEY=sk-... + OPENAI_API_KEY_FOR_LLM=false now returns "noop" from detectLlmProviderKind (was "llm" before the fix). - OPENAI_API_KEY=sk-... alone still returns "llm" (intended default). - npm run build clean. Note: 10 pre-existing test failures on test/mcp-standalone.test.ts are a stale-branch artefact — this branch is 10 commits behind main and is missing the MCP shim fixes that landed via #311 / #327. Recommend rebasing on main (or "Update branch" via the GitHub UI) before merge. * feat(providers): openai — fetch timeout, Azure auto-detect, OPENAI_API_KEY scope hint Three follow-ups against the v2 PR so reviewers can see what's left before merge: 1. Outbound fetch timeout. The other raw-fetch providers (anthropic / gemini / openrouter / minimax) all lack one too — that's a same-pattern repo-wide concern tracked as a follow-up issue. This PR fixes the bound on the new surface only: AbortController + setTimeout, default 60s, overridable via OPENAI_TIMEOUT_MS. Abort messages explain how to raise the bound. 2. Azure OpenAI auto-detection. Azure is in the README's supported list but the code only emitted the standard OpenAI shape. Now detects `.openai.azure.com` hostnames at construction time and: - swaps `Authorization: Bearer` for `api-key: <KEY>` - drops the `/v1` path prefix (deployment is baked into base URL) - appends `api-version=<version>` query param (env-overridable) Default api-version is `2024-08-01-preview` per Azure docs. Existing users of the standard OpenAI shape are unaffected — the detection is purely hostname-driven. 3. README clarity on `OPENAI_API_KEY` shared use. Embeddings (#186) and this PR's LLM both auto-activate from the same key, which surprises users who only meant one or the other. Added an explicit callout above the LLM section pointing at the `OPENAI_API_KEY_FOR_LLM=false` opt-out. Also documents the new timeout + Azure base-URL shape inline. Build clean. Tests 954/954 (10 pre-existing mcp-standalone failures on main are not in this branch's snapshot). --------- Co-authored-by: Rohit Ghumare <ghumare64@gmail.com> |
||
|
|
6f228bd8c0 |
fix(website): compare section polish (#427)
User caught the section looking bad on the live merge: 1. Title "AGENTMEMORY VS. THE FIELD." was redundant (eyebrow already reads VS.) and wrapped ugly with FIELD. dangling on its own line. Shortened to "VS. THE FIELD." Eyebrow + title now read together as "VS. · VS. THE FIELD." which is still on-brand and fits one line at every viewport above 480px. 2. Added `text-wrap: balance` to .section-title globally so titles that DO need to wrap break at a balanced point instead of leaving orphan words. Affects every section's heading; net win — already verified other titles (Features TWELVE THINGS, CommandCenter TWO UIs, Agents SIX FIRST-PARTY, etc) render correctly. 3. Comparison NATIVE PLUGINS cell value was "6 (Claude/Codex/ OpenClaw/Hermes/pi/OpenHuman)" — that's 49 chars stuffed into a ~180px grid column, forcing multi-line wrap that looked cramped. Cell now reads just "6"; the agent names are already visible in the dedicated Agents grid two sections above. 4. Row grid: bumped agentmemory column from 1fr → 1.3fr at the expense of the label column (1.6fr → 1.4fr) so the longer cell values (95.2%, YES (APACHE-2.0), 2 (Qdrant, Neo4j)) have breathing room. Added `word-break: break-word` as a safety net for future entries. Padded the row 24px horizontally so cells don't kiss the section edge. Build clean. |
||
|
|
f1fbea3c08 |
feat(website): hero rebuild — npx CTA + inline validation + SSR star count (#425)
Five user-reported issues against agent-memory.dev v0.9.16: 1. AGENT/MEMORY title was clamp(54px, 12vw, 160px) — eating ~half the viewport on most desktop widths and forcing FeaturedIn below the fold. Reduced to clamp(44px, 9vw, 124px). Mobile-stacked variant now clamp(40px, 14vw, 72px) — cleaner. 2. Primary CTA was a generic "START IN 30 SECONDS" button that anchored to #install. Replaced with a copyable terminal command — `$ npx @agentmemory/agentmemory` rendered as a gold-bordered cell with "CLICK TO COPY" affordance. Copy → clipboard, transient "COPIED" state. Matches what users actually type. 3. FeaturedIn was a standalone section below the fold. Now inlined inside the hero (compact mode — no border, no own padding, transparent background) so AlphaSignal / Agentic AI Foundation / Trendshift validation lands above the fold. 4. Star count was missing from the hero CTA button. The component only fetched client-side from unauthenticated github.com/api which is rate-limited to ~60 req/hr/IP — first paint had no count, often forever on mobile (which is what the user screenshot showed). Hero is now an async server component; calls lib/github.ts fetchRepoStats (same path Nav already uses), passes the SSR'd count into GitHubStarButton via new optional initialStars prop. Client-side refetch still fires in useEffect as a best-effort refresh. 5. Mobile layout was unverified. Tightened all clamp() values, added explicit @media (max-width: 760px) breakpoint that drops padding, stacks the title, and reduces the chip size. The new HeroNpxCommand has its own mobile breakpoint at 640px that hides the "CLICK TO COPY" hint and stretches to full width. CTA stacks vertically now: copyable command (primary, gold border) on row 1, then "SEE IT MOVE" + STAR-with-count on row 2. FeaturedIn strip sits below row 2 as part of the above-the-fold validation. Build clean. No new deps. |
||
|
|
fae26ca049 |
chore(telemetry): pin worker project_name in registerWorker options (#426)
iii-sdk's InitOptions.telemetry.project_name field falls back to auto-detection when omitted — cwd / package.json basename / hostname, in that order — which produces inconsistent identifiers per host (`agentmemory`, `node`, `npm`, occasionally the user's home dir basename when run via npx). Setting it explicitly to "agentmemory" gives every install the same stable project identifier in the engine's metrics + traces output, so downstream attribution and grouping read cleanly. Also pins `language: "node"` and `framework: "iii-sdk"` for the same reason — auto-detection of those two has the same divergence problem across install paths (npm global vs npx vs source build). No behavior change for users. No new deps. iii-sdk 0.11.6 exposes these fields under InitOptions.telemetry — see node_modules/iii-sdk/dist/index.d.mts:32-37 (TelemetryOptions). |
||
|
|
705321be34 |
docs: refresh current project stats (#330)
* docs: refresh current project stats * docs: update REST endpoint badge count * docs: refresh stats for v0.9.16 --------- Co-authored-by: 이민재 <19909783+honor2030@users.noreply.github.com> |
||
|
|
b48176cacd |
fix(mcp): use DELETE for governance memory deletion proxy (#423)
Co-authored-by: 이민재 <19909783+honor2030@users.noreply.github.com> |
||
|
|
b7e1240d06 |
chore(release): v0.9.16 — DevEx polish + website refresh (#417)
Patch bump per the established rule: additive surface only, no breaks to MemoryProvider trait, exported types, or default behaviour. New top-level subcommands (`--reset` already shipped 0.9.15, no new commands here) are opt-in. PRs included since v0.9.15: #408 — onboarding wires selected agents inline + memory-share callout #409 — clarify MCP is opt-in (REST primary) #410 — 5-port ready panel, iii console install, global-install prompt #411 — splash banner rerender + README install hoist + npx caveat #415 — agent-memory.dev refresh (FeaturedIn bar + Agents/Compare/ CommandCenter/Hero updates) Files bumped (9): package.json, packages/mcp/package.json, plugin/.claude-plugin/plugin.json, plugin/.codex-plugin/plugin.json, src/version.ts, src/types.ts, src/functions/export-import.ts, test/export-import.test.ts, CHANGELOG.mdv0.9.16 |
||
|
|
9ba346789b |
docs(website): refresh agent-memory.dev for v0.9.15+ (#415)
* docs(website): refresh agent-memory.dev for v0.9.15+ DevEx surface Drift accumulated across the website from v0.9.0 through v0.9.15. Each section caught up: CommandCenter: - "iii CONSOLE · OPTIONAL" → "iii CONSOLE · FIRST-CLASS". User pushback: the console gives engine-level visibility (workers, functions, queues, traces) and is now installed inline by the agentmemory CLI on first run from v0.9.16. Not optional. - Bullet "49 HTTP TRIGGERS" → "107 HTTP ENDPOINTS" to match generated-meta.json restEndpoints count. Dropped the stale "33 REGISTERED FUNCTIONS" subhead. - Section lede now mentions both UIs are first-class + inline- installed. Compare: - MCP TOOLS our column 44 → 51 (matches generated-meta.json). - New REST ENDPOINTS row (107) — competitors don't ship a REST shape per their docs, so dashes for mem0/letta/cognee. - New NATIVE PLUGINS row listing the 6 first-party agents + Cursor/Gemini CLI. Agents: - FEATURED expanded from 4 to 6 (added pi + OpenHuman). Codex CLI pitch updated to "6 hooks + MCP · native plugin" reflecting the hooks addition in PR #383. - Section title "FOUR FIRST-PARTY" → "SIX FIRST-PARTY". - Section lede now mentions `agentmemory connect <agent>` (shipped in v0.9.15 PR #402). Hero: - "START IN 60 SECONDS" CTA → "START IN 30 SECONDS". Cold install + engine spawn measured 8-12s on v0.9.15 with the native binary path; 60s was the v0.9.0-era Docker-first claim. generated-meta.json regenerated by the build (auto-derive rule). Website build clean. * chore(website): refresh generated-meta timestamp * feat(website): AS FEATURED IN bar — AlphaSignal, AAIF, Trendshift Wedges between Hero and Stats. Three text-cells in a 3-col row (1-col on mobile), each links to the source article. Matches the existing brutalist mono aesthetic — no logo files, no marquee animation, no JS. Border + small ↗ glyph on each card; gold border on hover. Sources chosen for brand authority + independence: - AlphaSignal (180K technical subscribers) → alphasignalai.substack.com/p/how-agentmemory-works-and-how-to - Agentic AI Foundation (Linux Foundation backed) → aaif.io - Trendshift "NEW 2026 · Position #19" → trendshift.io/repositories/25123 Static, server-rendered, no client bundle impact. * feat(website): real logos in AS FEATURED IN bar Replaced text-only treatment with actual brand marks: - AlphaSignal — github.com/Alpha-Signal org avatar (verified) https://avatars.githubusercontent.com/u/64016073?s=200&v=4 - Agentic AI Foundation — aaif.io's own favicon PNG (verified by fetching their site) - Trendshift — their official badge endpoint trendshift.io/api/badge/repositories/25123 — bakes the repo's live star + position count into the image, so the cell tracks the Trendshift state automatically Layout split: AlphaSignal + AAIF render logo-left / text-right with ↗ arrow. Trendshift renders centered with the full badge image (it's already a wide brand mark with the rank + stars baked in, so making it text-bound looks cramped). next.config.ts adds the three new image hosts to remotePatterns (aaif.io, trendshift.io, raw.githubusercontent.com). Existing unoptimized={true} per image so we don't proxy through Vercel's optimizer. Build clean. No new deps. * feat(website): use real AAIF wordmark logo + center 3-card grid User pushed the actual Agentic AI Foundation wordmark — 838x203 PNG on transparent background, black artwork. Dropped the favicon and self-hosted the wordmark at /featured/aaif-logo.png so the brand reads cleanly on the dark cell. Rendered as a badge-style cell (centered, wide-form) like Trendshift, with a `filter: invert(1) brightness(1.05)` so the black artwork inverts to white-on-dark and matches the site palette without shipping a second asset. Grid template balanced: 1fr / 1.3fr / 1fr — gives AAIF the extra horizontal room its wordmark needs while keeping AlphaSignal and Trendshift consistent on either side. Max width bumped to 1000px. `badgeImg` got `object-fit: contain` and an explicit `max-height` so the AAIF + Trendshift badges sit at the same vertical baseline. * fix(website): coderabbit PR #415 — meta drift + a11y + dead CSS Five findings, all valid against the branch: 1. CommandCenter line 48 hardcoded "107 HTTP ENDPOINTS" but website/lib/generated-meta.json reports restEndpoints: 121. Bumped to 121. The meta file is the source of truth (auto-derived from src/triggers/api.ts at build via scripts/derive-meta.js). 2. Compare.tsx REST ENDPOINTS row same drift: "107" → "121". 3. Compare.tsx NATIVE PLUGINS row claimed "6" in the label but the parenthesized list had 8 names (Cursor + Gemini included). Per README: those two are MCP-server tier, not native plugin. List trimmed to the actual 6 native-plugin agents: Claude/Codex/OpenClaw/Hermes/pi/OpenHuman. 4. FeaturedIn.module.css .cell had :hover only — keyboard users tabbing through saw no focus indicator (WCAG 2.4.7). Added :focus-visible with gold border + outline. 5. .cellBadge declared `grid-template-columns: none` while using display: flex. Dead property removed. |
||
|
|
7530339b4c |
feat(cli): 5-port ready panel + iii console install + global-install prompt + version-string fix (#410)
* feat(cli): 5-port ready panel, console install, global-install prompt
The boot/ready surface now reflects what's actually running and what's
on the menu instead of eliding three services and offering one passive
hint.
1. Five-port ready panel. printReadyHint switches from a single-line
"Memory ready on :3111 · viewer on ..." to a clack p.note panel
that lists REST API, Viewer, Streams, Engine bridge, and iii
console. Each port reads from its env var (III_REST_PORT,
III_VIEWER_PORT, III_STREAM_PORT / III_STREAMS_PORT,
III_ENGINE_PORT / III_ENGINE_URL) with sane defaults; nothing
is hard-coded.
2. iii console install probe + auto-install. New ensureIiiConsole
detects the binary via whichBinary("iii-console") or
~/.local/bin/iii-console. If absent on a TTY (not CI), prompt
the user; on yes shell out to "curl | sh"; on no persist
skipConsoleInstall: true. We do NOT probe the console's HTTP
port: its default 3113 collides with our viewer, so binary
presence is the right signal at boot time.
3. Global-install prompt replaces maybeEmitNpxHint. Users kept
ignoring the passive npx tip and then typing "agentmemory stop"
in a new shell to a "command not found". Now on first npx run
(TTY, not CI, not already skipped), we ask once and run
"npm install -g @agentmemory/agentmemory@<VERSION>" on yes. On
no, we persist skipGlobalInstall: true so we never re-prompt.
4. Version-string fix. The engine-version-mismatch warning quoted
"agentmemory v0.9.14+ pins ..." forever. Switched to the live
VERSION constant so the message stays honest across releases.
Preferences schema gains skipGlobalInstall and skipConsoleInstall
(both default false); schemaVersion stays at 1 because the reader
already merges over defaults.
* fix(cli): coderabbit PR #410 — ready panel host detection + runnable hints
Two findings, both valid against the just-shipped feat/cli-boot-panel
code:
1. The console line printed `(run: iii-console -p <port>)` regardless
of where the binary actually lived. We just detected the binary
path two lines earlier — print that as the executable hint
(`consoleState.binPath -p <port>`) so the suggestion is runnable
without assuming `iii-console` is on PATH.
2. REST / Streams / Engine URLs hardcoded `localhost`, so a deploy
that binds `III_ENGINE_URL=ws://remote-host:49134` would print
misleading addresses. Added `getEngineHost()` which reads
III_ENGINE_URL or AGENTMEMORY_URL and falls back to localhost.
REST now uses getBaseUrl() (already honors AGENTMEMORY_URL),
Streams + Engine read the new host helper. Viewer line untouched
— getViewerUrl() already handles this correctly.
3. Bonus runnability fix: the "Try: agentmemory demo" suggestion
assumed the bare command was on PATH. Now picks `npx
@agentmemory/agentmemory demo` when invoked via npx, falls back
to the bare form for global installs.
Build clean, 954/954 tests pass.
|
||
|
|
9344cfd502 |
fix(cli): rerender splash banner + README npx caveat (#411)
* fix(cli): rerender splash banner with clean figlet output + README npx caveat
Two small fixes from local v0.9.15 testing:
1. The hand-drawn ASCII in `src/cli/splash.ts` was rendering "agentmemory"
with broken middle glyphs — `_ _` instead of `_ __` around the 'n'
and merged 'tm/me' segments. Replaced with verified `figlet
agentmemory` standard-font output (70 cols, 6 rows, regenerable).
Compact + minimal banners untouched.
2. Added a "Recommended: install globally" section to README's Quick
Start. Explains the npx per-version cache gotcha (running
`npx ...@0.9.14` then `npx @agentmemory/agentmemory` may serve the
stale cached version) and the three workarounds:
- `npm install -g @agentmemory/agentmemory` (recommended)
- `npx -y @agentmemory/agentmemory@latest` (forces fresh fetch)
- `rm -rf ~/.npm/_npx && npx ...` (one-shot cache clear)
Notes that v0.9.16 ships an interactive global-install prompt
inline on first npx run.
Build clean (44.46 kB cli.mjs). All 954 tests pass.
* docs: hoist install section to top of README + update website Install card
README: install was buried at line 306 below benchmarks, comparison
tables, and the agents grid. Moved a 4-line install block right under
the nav anchors at the top, with the bare `agentmemory` command from
the global install path leading. Kept the deeper Quick Start section
below — this is the "front door" version.
website (Install.tsx): the three-card install flow now leads with
`npm install -g @agentmemory/agentmemory` (step 1), then bare
`agentmemory` + `agentmemory demo`. Section title changed from "THREE
COMMANDS. ANY AGENT." to "ONE INSTALL. ANY AGENT." and the lede notes
the npx fallback for users who want zero-install.
website/lib/generated-meta.json refreshed by the build (auto-derived
from package.json + walked test count; pre-existing feedback rule).
Build clean (website + main both pass).
* docs: address coderabbit PR #411 review
README: annotate `rm -rf ~/.npm/_npx` as POSIX-only, point Windows
users at `Remove-Item ...\npm-cache\_npx`. Mark `npx -y @...@latest`
as the cross-platform option. Applies to both install-section and
quick-start blocks (lines 87, 363).
website Install.tsx: hint on step 1 now says "STEPS 2/3 NEED THIS"
so users who skip can't get stuck on `agentmemory` not found. Added
a fourth card "PREFER ZERO-INSTALL? USE NPX" with the bare npx
command and a pointer to the README caveat — covers the skip path
without making step 1 ambiguous.
Skipped (verified-false): coderabbit flagged the splash banner's
"70 columns wide" comment as wrong (claimed rows 3+5 are 76/77). They
counted source-string chars including the `\\` escapes; runtime is 70
on all 6 rows (node length check), which is what alignment cares
about. Comment is accurate, no change needed.
|
||
|
|
48070ad096 |
feat(cli): onboarding wires selected agents inline + memory-share callout (#408)
After the agents multi-select, show a callout explaining that all picked agents share the same memory at :3111 (Claude Code saves visible to Codex + Cursor instantly). After provider selection + .env write, ask whether to run agentmemory connect <agent> for each selected agent right now. On yes, dispatch through the existing connect adapter (same code path as agentmemory connect <agent>) and summarize wired vs manual vs failed. On no/cancel, print the exact commands to run later. Expose runAdapter from src/cli/connect/index.ts so onboarding reuses the same install path the CLI uses today. |
||
|
|
6e3c052943 | feat(cli): clarify MCP is opt-in — REST primary, MCP for MCP-only clients (#409) | ||
|
|
c3a613aed8 |
chore(release): v0.9.15 — DevEx overhaul (splash, connect, doctor v2, remove, silent killers) (#407)
Patch bump per the established rule: all changes additive, no breaks to MemoryProvider trait or exported types or default behaviour. New top-level subcommands (connect, remove, --reset, --force) are opt-in. PRs included since v0.9.14: #405 — silent killers: viewer port auto-bump, engine version-match warning, stop --force, adopt-on-attach, npx PATH hint #402 — agentmemory connect — automate native-plugin install for 8 agents (claude-code, codex, cursor, gemini-cli, openclaw end-to-end; hermes/pi/openhuman stubbed) #406 — interactive doctor v2 + agentmemory remove (destruction plan + two-confirmation flow) #403 — splash banner + agent grid + provider picker + smart-defaults preferences + bootLog shim (30+ lines of log spam → 10) Files bumped (9): package.json, packages/mcp/package.json, plugin/.claude-plugin/plugin.json, plugin/.codex-plugin/plugin.json, src/version.ts, src/types.ts, src/functions/export-import.ts, test/export-import.test.ts, CHANGELOG.mdv0.9.15 |
||
|
|
659d0c3e1a |
feat(cli): splash banner, agent grid, provider picker, smart defaults (#403)
Replaces the 25-line `[agentmemory] X` log spam on first invocation
with a SkillKit-grade onboarding flow:
* `src/cli/splash.ts` — terminal-width-aware ASCII banner with three
tiers (>=120 / 80-119 / <80 cols), brand-accent orange via ANSI
256-colour, NO_COLOR-aware, no new deps.
* `src/cli/preferences.ts` — JSON-backed prefs at
`~/.agentmemory/preferences.json`, atomic write via tmp+fsync+rename,
graceful defaults on corrupt/missing file, `isFirstRun()` /
`resetPrefs()` helpers. 7 unit tests added.
* `src/cli/onboarding.ts` — first-run multi-select for agents (native
plugin + MCP-server rows from the README), single-select for
provider (anthropic / openai / gemini / openrouter / minimax / skip
-> BM25-only), seeds `~/.agentmemory/.env`, prints the next-step
hint.
* `src/logger.ts` — adds `bootLog` / `setBootVerbose`. The 18 startup
`console.log` calls in `src/index.ts` route through this shim;
default is muted, `--verbose` (or AGENTMEMORY_VERBOSE=1) restores
the old chatty output. Warnings, errors, and shutdown logs stay
loud.
* `src/cli.ts` — splash before clack intro, run onboarding when
`isFirstRun()` or `--reset`, propagate verbosity to the worker
logger, wait for `/agentmemory/livez` after `import("./index.js")`
and print a single-line ready hint. Adds `--reset` flag.
First-run output drops from 30+ lines to ~10. Subsequent runs are a
single ready line. `--verbose` restores the old verbose surface for
debugging. Build passes; the 10 pre-existing failures in
`test/mcp-standalone.test.ts` are unchanged (903 -> 910, all 7 new
passes are `test/preferences.test.ts`).
|
||
|
|
29e8865286 |
feat(cli): interactive doctor v2 + agentmemory remove (#406)
* feat(cli): interactive doctor v2 with inline fixes Replace passive doctor with an interactive [F]ix/[S]kip/[?]more/[Q]uit prompt per diagnostic. Each check now ships a one-line problem, cause, fix-preview, and inline apply() — and we re-check only the affected diagnostic after a fix, not the whole suite. New flags: doctor --all apply every fix without prompting (CI) doctor --dry-run show what each fix would do, run nothing Catalog (src/cli/doctor-diagnostics.ts): - env-missing → runInit() - no-llm-provider-key → open .env in $EDITOR - engine-version-mismatch → runIiiInstaller + restart - viewer-unreachable → stop + restart engine - stale-pidfile → clear pid + state, restart - env-placeholder-keys → open .env in $EDITOR - iii-on-path-not-local-bin → manual (suggest installer) The catalog is exported as a pure data structure so tests can assert each entry has check/fix/message without booting clack. Passive server checks (graph populated, Claude Code hooks, flags) still run first — they need a live engine and have no one-shot inline fix. * feat(cli): add agentmemory remove for clean uninstall New command tears down everything agentmemory installs: - ~/.agentmemory/iii.pid + engine-state.json (after stopping engine) - ~/.agentmemory/.env (asks separately — holds API keys) - ~/.agentmemory/preferences.json - ~/.agentmemory/backups/ (connect-PR manifest + backups) - ~/.local/bin/iii (only when iii --version matches our pin) - Any agent symlinks from the connect-manifest - ~/.agentmemory/data/ (asks separately, default keep) Surface: agentmemory remove # interactive, double-confirms agentmemory remove --force # skip generic confirms agentmemory remove --keep-data # remove binaries + symlinks only Plan building lives in src/cli/remove-plan.ts as a pure fs-inspecting function so tests sandbox a fake $HOME under tmpdir() and assert on plan shape without touching the real home directory. Always-ask items (.env, memory data, mismatched-version local-bin iii) still get per-item confirmation even with --force. |
||
|
|
b36a6d7c73 |
feat(cli): add agentmemory connect to automate native-plugin install for 8 agents (#402)
agentmemory connect <agent> wires agentmemory into each supported coding agent: detect install, back up existing config to ~/.agentmemory/backups/, merge agentmemory MCP entry, verify, idempotent on re-run. Working: claude-code, codex, cursor, gemini-cli, openclaw. Stubbed with manual-install hints: hermes (YAML), pi (TS extension copy), openhuman (no integrations folder). Windows prints manual-install message and exits. Argv: agentmemory connect [agent] [--all] [--dry-run] [--force] |
||
|
|
2250d8677d |
fix(cli): silent killers — viewer port auto-bump, engine version-match warning, adopt-on-attach, stop --force, npx PATH hint (#405)
* fix(cli): auto-bump viewer port when 3113 is taken The viewer used to log 'Viewer port 3113 already in use, skipping viewer.' and silently fail. On every fresh terminal you'd lose the viewer for the rest of the session unless you noticed the warning buried in stdout. Retry the next 10 ports (3113 -> 3122) before giving up. On fallback, log: 'Viewer started on http://localhost:3114 (fallback from 3113)'. * fix(cli): warn when iii-engine on PATH is not the pinned version agentmemory pins iii-engine to v0.11.2 (see IIPINNED_VERSION in src/cli.ts) but anyone with a different iii on PATH gets silent behavioral drift -- EPIPE reconnect loops, empty search after save -- because we never tell them their engine doesn't match. Add warnIfEngineVersionMismatch() and fire it from both: - startIiiBin() before we spawn (covers fresh starts) - main() early-return when isEngineRunning() returns true (covers attach-to-existing) The warn tells the operator how to silence (AGENTMEMORY_III_VERSION) and how to downgrade (curl | tar). * fix(cli): add stop --force to bypass Docker-heuristic guard runStop() correctly refuses to signal host PIDs when state file is missing and a docker-compose.yml is discoverable -- the engine might have been started by another shell via Docker compose, and SIGTERMing the lsof match would kill an unrelated host process. But when the engine actually WAS started natively (e.g. by a stale agentmemory v0.9.13 with no state-writing) and the state file is gone, the guard becomes a foot-gun: stop refuses, the engine stays up, and the operator has no escape hatch short of `lsof | kill`. Add --force. Documented in --help. The non-force path remains the default and still refuses. * fix(cli): adopt running engine on attach (write pidfile + state) When main() finds isEngineRunning() already true (e.g. a previous shell started it), we early-return without writing iii.pid or engine-state.json. Later `agentmemory stop` then hits the Docker-heuristic guard (state missing + compose discoverable) and refuses to act -- correct in principle, foot-gun in practice. Adopt on attach: write iii.pid from lsof-on-port and engine-state.json as kind=native attached=true, but only if neither already exists. Idempotent -- safe to re-run. Log: 'Attached to existing iii-engine (pid 12345)'. * fix(cli): hint that global install drops the `npx` prefix After the engine is ready, if invoked via npx (detected via npm_lifecycle_event / process.argv[1] containing _npx / npm_config_user_agent starting with npm/), emit one line: Tip: install globally for the bare `agentmemory` command: npm install -g @agentmemory/agentmemory Suppressed by ~/.agentmemory/preferences.json with skipNpxHint:true (a sibling PR will write that file when the user opts out). |
||
|
|
372c6a6bbb |
chore(release): v0.9.14 — CLI installer first + agentmemory stop + Docker-aware teardown (#401)
Patch bump. No breaking changes to the public API or the existing CLI
subcommands. CLI bootstrap flow changes are additive; existing Docker
users keep working via the new AGENTMEMORY_USE_DOCKER=1 opt-in.
Files bumped (9):
- package.json
- packages/mcp/package.json
- plugin/.claude-plugin/plugin.json
- plugin/.codex-plugin/plugin.json
- src/version.ts
- src/types.ts (ExportData.version union — also adds 0.9.13
which was missed in the previous release)
- src/functions/export-import.ts (supportedVersions Set)
- test/export-import.test.ts
- CHANGELOG.md
PRs included since v0.9.13:
#396 — feat(cli): install iii-engine binary first, Docker opt-in;
agentmemory stop with Docker-aware teardown; LISTEN-only
lsof filter so the CLI no longer signals its own parent.
#397 — docs(readme): move OpenHuman after pi in agents grid.
v0.9.14
|
||
|
|
9faf73760c |
feat(cli): install iii-engine binary first, Docker opt-in (#396)
* feat(cli): install iii-engine binary first, Docker opt-in Reorder startEngine() fallback so the native iii binary is the happy path. Docker compose becomes opt-in via AGENTMEMORY_USE_DOCKER=1, an explicit prompt choice, or CI mode falls back to it only when auto-install fails. Fixes a class of "engine started but REST never responded" failures observed on Rancher Desktop, where docker compose returns 0 on a degraded pull and we wait 15s for /livez against nothing. New tier order: 1. iii on PATH 2. ~/.local/bin/iii / fallback paths 3. clack select (install / docker / manual) -- auto-install in CI 4. Docker compose only via explicit opt-in 5. fail-loud with install instructions Extracts the installer logic out of runUpgrade into runIiiInstaller() so first-run and upgrade share the same pinned-v0.11.2 curl+tar path. Adds an `agentmemory stop` command: - writes ~/.agentmemory/iii.pid from spawnEngineBackground - SIGTERM with 3s wait, escalates to SIGKILL (iii ignores SIGTERM) - lsof :3111 fallback when pidfile is stale or missing - clears pidfile on success Engine persistence is unchanged: `detached: true` + `child.unref()` means memories keep flowing after npx exits. A second `npx` short- circuits at isEngineRunning() before re-prompting. Tightens installInstructions() copy: drops the AGENTMEMORY_III_VERSION rationale tail (now in --help) and reorders so the curl path leads, Docker is path B with the opt-in env var. Help: documents stop, AGENTMEMORY_USE_DOCKER, AGENTMEMORY_III_VERSION. * fix(cli): make agentmemory stop Docker-aware and crash-safe Two issues caught in PR #396 review: 1. Docker-started engine would let runStop kill host proxies. `findEnginePidsByPort` returned every PID holding :3111. When the engine was started via `docker compose up -d`, the listening socket lives inside the container; on the host, lsof reports the Docker socket proxy (`com.docker.backend`, vmnetd, etc.). The stop flow would happily SIGTERM/SIGKILL those, taking down Docker Desktop networking for every other container the user has running. Adds an EngineState record at ~/.agentmemory/engine-state.json written by startEngine at spawn time (kind=native|docker, configPath or composeFile). runStop checks state first: - kind=docker: run `docker compose -f <file> down`, never signal host PIDs. Verify the docker binary is still on PATH and the compose file still exists; otherwise print the exact command the user should run. - kind=native: existing pidfile + lsof flow. - state missing + compose file discoverable + port held + no pidfile: refuse to signal blind, point at `docker compose down`. Catches engines started by a different shell or older agentmemory build that didn't write state. 2. runStop cleared the pidfile when isEngineRunning() returned false, even if a stale process was still bound to the port. The HTTP probe can fail while the engine is hung, paused (SIGSTOP), or in a half-closed state. Clearing the pidfile in that case made the next run silently start a second engine on a conflicting port. Now we keep the pidfile and surface the live PIDs with `ps -p` + `lsof` hints so the user can investigate before manual cleanup. 3. Bonus fix: `lsof -i :3111 -t` also returns CLIENT PIDs, not just the LISTEN socket owner. The CLI's own keep-alive fetch in isEngineRunning() showed up as a client, so signalAndWait would SIGKILL its own parent — exit code 137, files never cleaned up. Restrict to `-sTCP:LISTEN` and filter out `process.pid` as a belt-and-braces guard. Verified with four scenarios on a real iii engine: - native + pidfile + state: SIGTERM → SIGKILL → exit 0, state cleared - docker state pointing at missing compose: refusal, state preserved, engine untouched - paused engine (kill -STOP): preserves pidfile, surfaces PIDs - lsof self-pid filter: CLI no longer kills itself Build: dist/cli.mjs 44.46 kB. Tests: 892/903 (11 pre-existing mcp-standalone failures unrelated to CLI changes). |
||
|
|
471cc21492 |
docs(readme): move OpenHuman after pi in agents grid (#397)
Reorder row 1 of the works-with-every-agent table so OpenHuman sits in the sixth slot (after pi) instead of the third. Keeps the native-plugin agents grouped left, leaves OpenHuman next to pi so the native-Memory-trait callout reads cleanly alongside the other native-integration agents. |
||
|
|
ff3e024d7d |
docs(readme): clarify MCP shim vs full server tool count (#233 gap 1) (#391)
The README implied the `@agentmemory/mcp` shim exposes "all 51 tools when AGENTMEMORY_TOOLS=all". In reality: - `AGENTMEMORY_TOOLS` is a server-side flag (read by iii-engine), not by the shim — setting it in the shim's MCP `env` block does nothing. - The shim has two modes: proxy (when `AGENTMEMORY_URL` reaches a running agentmemory server, all 51 tools are forwarded) and local fallback (no server reachable → 7 hardcoded tools from `src/mcp/standalone.ts`'s `IMPLEMENTED_TOOLS`). This caused users (Cursor / OpenCode / Gemini CLI) following the README to assume they had misconfigured something when they only saw 7 tools. Two changes: - Codex plugin bullet: replace the misleading `AGENTMEMORY_TOOLS=all` qualifier with an accurate proxy/local mode description. - MCP Server section header: add a callout that explicitly enumerates the 7-tool local fallback set and tells users how to unlock the full 51. Addresses gap #1 in #233. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
eb5f7e6895 |
chore(deps): bundle 10 Dependabot bumps (claude-agent-sdk 0.3, dotenv 17, vitest 4, ts 6, types/node 25, react 19.2.6, checkout/setup-node v6) (#390)
* chore(deps): bundle 10 Dependabot bumps + README agents grid refresh Closes the open Dependabot wave in one PR. Root npm bumps: - @anthropic-ai/claude-agent-sdk ^0.2.56 → ^0.3.142 (closes #389 group) - dotenv ^16.4.7 → ^17.4.2 (closes #356) - @types/node ^22 → ^25.7.0 (closes #357) - typescript ^5.7 → ^6.0.3 (closes #358) - vitest ^3 → ^4.1.6 (closes #359) Website npm bumps: - @types/node 22.10.2 → ^25.7.0 (closes #354) - typescript 5.7.2 → ^6.0.3 (closes #353) - react/react-dom ^19.2.5 → ^19.2.6 (closes #352 group) github-actions bumps: - actions/checkout v4 → v6 (closes #351) - actions/setup-node v4 → v6 (closes #350) Verified: - npm run build clean against new TypeScript 6 + vitest 4 stack - npm test 903 / 903 pass - website npm run build clean (next 16.2.6 + react 19.2.6 + ts 6 stack generates 5 static pages, finalises optimisation) - No source changes were needed for any breaking-version bump — the agent-sdk 0.3.x removal of unstable_v2_createSession etc. is not on any path agentmemory exercises (we use query() + the existing AgentSDKProvider shape). Plus README refresh on the Works-with-every-agent grid: - Added OpenHuman entry (tinyhumansai/openhuman) pointing at the recently-landed Memory trait backend (PR tinyhumansai/openhuman#1743) - Added pi as a native-plugin entry (we already ship integrations/pi/security.ts and the plugin file in-tree) - Reordered the grid so native-plugin agents sit in row 1 and MCP-only / REST-only agents sit in row 2 - Updated sub-text on Claude Code / Codex CLI / OpenClaw / Hermes / pi to consistently say "native plugin" rather than mixing "plugin", "skills", "hooks". Counter on assets/tags/section-agents.svg moved from "16 integrations" to "15 integrations" because we dropped the meta "Any agent" + "Claude SDK" cells that double-counted REST and the AgentSDKProvider path already represented by Claude Code. * docs(readme): swap pi logo to first-party SVG Drops the generic GitHub-org avatar for the brand-mark SVG. Lives in assets/agents/ so the README + future grid renders reach it directly. |
||
|
|
8b98432853 |
chore(release): v0.9.13 — env-example + init + CJK + load bench + deploy templates + Gemini GA bumps (#383)
* chore(release): v0.9.13 — env-example discovery + CJK tokenizer + load harness + deploy templates + Gemini GA bumps + 14 advisories closed Six PRs landed since v0.9.12: - #372 .env.example discovery (this commit) — repo-root template + `init` CLI command + CI sync-checker - #362 CJK BM25 tokenizer (`@node-rs/jieba` + tiny-segmenter + Hangul) - #363 `benchmark/load-100k.ts` harness with p50/p90/p99 + per-release results dir - #361 one-click deploy templates for fly.io / Railway / Render / Coolify (multi-stage Dockerfile, `iiidev/iii` base, `gosu` privilege drop, first-boot HMAC, verified end-to-end on fly.io) - #364 Python ecosystem via `iii-sdk` example (replaces closed PR #360) - #370 Gemini GA bumps (LLM default → gemini-2.5-flash, embedding → gemini-embedding-001 + L2-norm + 768 dims) Plus 14 open Dependabot advisories closed in PR #348 via Next.js → 16.2.6 and PostCSS → 8.5.10 overrides. Bumped: - src/version.ts: VERSION 0.9.12 → 0.9.13 - package.json: 0.9.12 → 0.9.13, files += ".env.example", build script copies .env.example into dist/ - packages/mcp/package.json: 0.9.12 → 0.9.13 (lockstep with main) - plugin/.claude-plugin/plugin.json, plugin/.codex-plugin/plugin.json: 0.9.12 → 0.9.13 - src/types.ts: ExportData.version union extended with "0.9.13" - src/functions/export-import.ts: supportedVersions Set extended - test/export-import.test.ts: expected version updated New surface: - .env.example at repo root — every env var read by src/ documented in one place, grouped by surface (LLM, embedding, auth, search tuning, behaviour flags, CLI runtime, ports, iii engine pin, Claude Code bridge, Obsidian export). Every line commented out by default so the file is a template. - agentmemory init — copies bundled .env.example to ~/.agentmemory/.env if absent, refuses to overwrite, prints a diff command. Wired into CLI dispatch + help block. - scripts/check-env-example.mjs — walks src/ for env-read patterns, fails CI on drift in either direction. Plugged into ci.yml after npm test. Initial bootstrap: 60 keys in sync. Verified: npm test 903/903, npm run build clean, init smoke pass (creates ~/.agentmemory/.env on first run, refuses overwrite on second). * fix(init): atomic copy via COPYFILE_EXCL; address CodeRabbit review Two valid findings from the CodeRabbit pass on PR #383. 1. `runInit` race between existsSync(target) + copyFile(template, target). A parallel `agentmemory init` (or any other process touching ~/.agentmemory/.env between the two calls) would silently overwrite the config the operator just wrote. Switch to a single atomic `copyFile(template, target, fsConstants.COPYFILE_EXCL)` and treat the EEXIST error as the "already configured" signal — same warning + diff hint as before, but the check + copy now happen in one syscall so they cannot race. Other failure paths still surface as process exit 1. 2. Comment on `scripts/check-env-example.mjs::walk` claimed it matched ".ts / .mts / .mjs" but the regex also matched ".js". Rewrote the comment to match the regex (".ts / .mts / .mjs / .js"). Same comment pass: noted that test/ never enters because the walk is rooted at src/, not because of an explicit skip. Skipped findings: - WHAT-style comment on `findEnvExample` — kept a one-liner explaining the package-vs-source priority since both paths are real; reduced the block from 4 lines to 2 instead of removing it entirely. - "Add trailing newline to .env.example" — file already ends with `\n` (verified `tail -c 5` shows `tion\n`). Verified locally: - `npm run build` clean. - `npm test` 903 / 903 pass. - First `agentmemory init` against a clean HOME creates the file. - Second init against the same HOME hits EEXIST and prints the "leaving it untouched" warning + diff hint without overwriting. - `node scripts/check-env-example.mjs` — in sync (60 keys).v0.9.13 |