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 | |
|---|---|---|---|---|
|
|
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 |
||
|
|
4b354b7bd8 |
chore(providers): bump Gemini defaults to current GA models (#370)
* chore(providers): bump Gemini defaults to current GA models Bundles two upstream PRs into one chore — both are blocking real users today and both are simple default-string bumps with no API contract change. LLM default (was PR #368, @yut304) - `gemini-2.0-flash` is deprecated in Google's Gemini API and returns 429 rate-limit errors under load. Replace the default with `gemini-flash-latest`. Users on a pinned `GEMINI_MODEL` in `~/.agentmemory/.env` are unaffected. Embedding default (was PR #246, @AmmarSaleh50) - `text-embedding-004` is deprecated (shutdown Jan 14 2026). Replace with `gemini-embedding-001` (GA): 100+ languages, MRL dims (768 / 1536 / 3072), 2048-token input. - URL path changes from `:batchEmbedContent` to `:batchEmbedContents` (plural — the new model's batch endpoint). - Each request now sends `outputDimensionality: 768` so the returned vectors match the existing index dim guard from #248 — no reindex needed. - L2-normalize each returned vector before pushing to the result array. `gemini-embedding-001` does not normalize by default, unlike `text-embedding-004`. Without this the cosine-similarity math elsewhere in the search pipeline (which assumes unit-length vectors) collapses. Verified - `npm test` clean: 903 / 903. - `npm run build` clean. Closes #368, closes #246. * fix(gemini): pin LLM default to gemini-2.5-flash + warn-once on zero-norm Addresses CodeRabbit findings on PR #370. 1. Pin Gemini LLM default to gemini-2.5-flash. `gemini-flash-latest` is a moving alias that points to whatever Google promotes next. Production behaviour should be deterministic from a release perspective — users who upgrade agentmemory should not also get a Gemini model rotation in the same step. Switch the default to the current stable GA model `gemini-2.5-flash`. Users who want the moving alias keep getting it via `GEMINI_MODEL=gemini-flash-latest` in `~/.agentmemory/.env`. 2. Warn-once on zero-norm embedding in l2Normalize. `gemini-embedding-001` can return a zero-norm vector for degenerate input. The previous code silently returned the zero vector — downstream cosine-similarity math then divides by zero and the call site sees `NaN` scores with no signal as to why. Emit a one-time stderr warning naming the model + vector length so operators can correlate index quality dips with upstream embedding regressions. Behaviour otherwise unchanged: return the zero vector and let BM25 carry the search signal. Throwing was the other option — rejected because a single bad embedding in a 100-item batch would abort the whole batch and surface as an indexing pipeline halt. Soft-fail + warn matches the rest of the embedding provider error handling. Skipped finding: - `outputDimensionality` → `output_dimensionality` snake_case rename. CodeRabbit asserts the REST API expects snake_case. The Gemini REST API actually uses camelCase on the wire — confirmed against ai.google.dev/api/embeddings (field labelled `outputDimensionality` in the REST schema; the Python SDK alone uses snake_case and translates internally). Current code is correct as-shipped; the snake_case rename would silently break the dim override. Verified: 903 / 903 tests pass; build clean. |
||
|
|
a9c3a59da8 |
feat(deploy): fly.io / Railway / Render one-click templates (#361)
* feat(deploy): add fly.io / Railway / Render one-click templates Closes #343. Lets operators stand up agentmemory on managed infrastructure without rolling their own Docker host. Each template extends the published rohitghumare64/agentmemory:latest image, mounts persistent storage at /data, generates the HMAC secret on first boot via openssl rand into /data/.hmac (chmod 600, printed once to stdout), and ships AGENTMEMORY_REQUIRE_HTTPS=1 by default so the v0.9.12 plaintext-bearer guard refuses to leak tokens over non-loopback HTTP if a TLS upstream gets misconfigured. Only port 3111 (REST API) is exposed publicly. The viewer on 3113 stays bound to localhost inside the container; every README documents the SSH-tunnel pattern. The main README gains a Deploy section with three Deploy-to-platform shield buttons linking to the platform's URL-encoded one-click flow, plus the deploy/ subtree with per-platform docs covering HMAC capture, rotation, /data backup, viewer access, cost floor, and known caveats. * fix(deploy): run as non-root, placeholder app names, drop invalid Render badge Addresses CodeRabbit findings on PR #361. Dockerfiles (fly/railway/render): drop `USER root` + RUN chmod — those left the container running as root after build. Replaced with `COPY --chmod=0755 --chown=65532:65532` so the entrypoint script lands with the correct permissions and owner without ever switching off the distroless image's default nonroot user. Added an explicit `USER 65532:65532` directive after COPY to make the runtime user intent-visible to reviewers. fly/README.md: every command referenced the hardcoded app name `agentmemory`. Since the global `agentmemory` slug on Fly is almost certainly taken, the first `fly launch --name agentmemory` fails and the rest of the volume/log commands point at a non-existent app. Walk users through setting `APP="agentmemory-$(whoami)"` once at the top and reference `$APP` everywhere (launch / volumes / logs / proxy / ssh / backup) — and derive the volume name (`agentmemory_data` style) from `$APP` so it stays consistent. railway/README.md: the previous `railway ssh --service agentmemory -- -L 3113:localhost:3113` snippet was invalid — Railway's CLI ssh does not support port forwarding. Replaced with a quick in-container curl check plus two valid browser paths: Railway TCP Proxy (the recommended option, dashboard → Networking → TCP Proxy on container port 3113) and an in-container sshd over a TCP Proxy if true SSH tunneling is needed. README.md: removed the "Deploy to Render" badge. Render's deploy URL requires `render.yaml` at the repository root, which we keep clean. The `deploy/render/` blueprint stays in-tree for users who set up the Render Blueprint flow manually — the deploy section now points readers there explicitly. Skipped: the entrypoint.sh SECRET-shape regex check. `openssl rand -hex 32` under `set -eu` already exits non-zero on any failure path and always emits exactly 64 hex characters when it succeeds — a post-hoc regex would only fire on a kernel-level surprise we can't recover from anyway. * fix(deploy): self-contained Dockerfiles (no phantom base image) Templates previously did `FROM rohitghumare64/agentmemory:latest`, an image that does not exist on Docker Hub. agentmemory ships via npm (`@agentmemory/agentmemory`) and runs against the `iii` engine binary fetched from the `iii-hq/iii` GitHub release. There is no first-party agentmemory Docker image yet. Rewrote all three Dockerfiles to be self-contained against `node:22-slim`: - `apt-get` curl / openssl / ca-certificates / tini for the runtime prereqs (openssl for the first-boot HMAC, tini for clean PID 1). - Download the iii binary from `github.com/iii-hq/iii/releases/download/iii/v${III_VERSION}/...` matching the build host's arch (uname -m). - `npm install -g @agentmemory/agentmemory@${AGENTMEMORY_VERSION}` with `--omit=optional` so the CJK native deps (added in PR #362) don't fail the build on platforms without musl/glibc prebuilds. - `mkdir -p /data && chown node:node /data` so the existing first-boot entrypoint can write `/data/.hmac` without root. - `USER node:node` (UID 1000, the standard non-root user in the node image) instead of the made-up `65532:65532` that assumed a distroless base. Both `AGENTMEMORY_VERSION` and `III_VERSION` are `ARG`s so platform operators can pin a specific release through their dashboard's build-args UI without editing the Dockerfile. README updates: - Top-level `README.md` and `deploy/README.md` no longer claim users pull a pre-published image; the wording now matches what each template actually does (build from source on the platform's builder). - Per-platform "Known caveats" sections drop the `rohitghumare64/agentmemory:latest` line and replace it with notes about build-time, cache reuse, and how to pin via build args. - `deploy/render/README.md` drops the obsolete `&imgURL=...` Deploy Hook query-string trick and replaces it with `AGENTMEMORY_VERSION` build-arg guidance. - `deploy/README.md` switches "Integration clients (Hermes, OpenClaw, pi)" → "Integration plugins" to match the scrub convention from the earlier PR-body edit. * feat(deploy): add Coolify self-hosted template Adds deploy/coolify/ alongside the existing fly / Railway / Render templates. Coolify (https://coolify.io/self-hosted) is the self-hosted-on-your-own-VPS option for operators who don't want their memories on a third-party managed plane. Same self-contained Dockerfile pattern as the other three (node:22-slim + iii binary + npm package, USER node, build-args for version pinning) plus a docker-compose.yml that Coolify's "Docker Compose" build pack consumes directly. Compose layer adds a HEALTHCHECK, a named volume for /data, and json-file log rotation matching the rest of the deploy surface. README walks the operator through: - Coolify dashboard flow (New Application -> Public Repository -> Docker Compose build pack -> Base Directory: deploy/coolify). - Capturing the first-boot HMAC secret from the Logs tab. - Viewer access via SSH tunnel from the Coolify host (port 3113 stays internal by default) and the optional second-domain + basic-auth pattern for sharing it. - HMAC rotation, /data backup paths (Restic / Borg / rsync / Coolify Backups), and VPS cost-floor pointers (Hetzner CX22, DO Basic Droplet, Vultr). Top-level deploy/README.md and README.md updated to list the new template and explain when to pick it (already running a VPS, want a self-hosted control plane, don't want a third-party host holding your memories). * fix(deploy): COPY iii from iiidev/iii image, drop tarball download Replaces the curl + tar + chmod sequence in all four Dockerfiles (fly / railway / render / coolify) with a single multi-stage COPY --from=iiidev/iii:${III_VERSION} /app/iii /usr/local/bin/iii. Why: - Drops the supply-chain risk CodeRabbit flagged on PR #361 (curl-and-extract without checksum verification). iiidev/iii is the same image agentmemory's docker-compose.yml already pulls — using Docker Hub's content-addressed manifest as the integrity boundary is the same trust we already extend on the local Compose path. - Drops the arch case statement (BuildKit picks the right manifest entry automatically from the multi-arch tag). - Drops 3 of the 4 apt-get packages from the bookkeeping side; curl stays for the eventual HEALTHCHECK probe. The iii binary lives at /app/iii inside the distroless iiidev/iii image (per github.com/iii-hq/iii engine/Dockerfile), and COPY into node:22-slim lands it at /usr/local/bin/iii with 755 permissions so any USER can exec it. ARG III_VERSION is now declared before the first FROM so it can be interpolated into the iii-image stage tag. ARG AGENTMEMORY_VERSION stays in the final stage where the npm install consumes it. * fix(deploy): rewire iii config + /data perms + platform port binding Audit against fly.io / Railway / Render / Coolify docs and the agentmemory CLI source surfaced four bugs that would prevent every template from actually serving traffic on a managed host. This commit addresses all four together because they share the same fix surface (entrypoint + Dockerfile + platform config) and shipping them independently would leave intermediate states broken. 1. iii config bound 127.0.0.1 + used relative ./data paths. The npm-bundled @agentmemory/agentmemory/dist/iii-config.yaml hard- codes `host: 127.0.0.1` on iii-http and `file_path: ./data/...` on iii-state / iii-stream. agentmemory CLI's findIiiConfig() returns the first existing candidate from a 3-item list with the npm bundle at position 0, so a sibling iii-config.yaml in cwd does not override. On any managed platform the result is the REST API binding loopback (router can't reach) and state writing to a directory that's not the persistent mount. Fix: the entrypoint overwrites the bundled file at boot with a container-tuned config (host 0.0.0.0, /data/... absolute paths, no iii-exec because the CLI orchestrates the worker over WS). This needs the entrypoint to run as root, hence the Dockerfile change below. 2. /data volume permissions root:root vs USER node. Every managed platform mounts persistent volumes root-owned 0755. The earlier Dockerfile ran `RUN mkdir -p /data && chown -R node` at build time, but the volume overlay at runtime replaces /data with the platform's empty root-owned mountpoint, leaving the `node` USER unable to write state_store.db / .hmac. Fix: stay USER root through the image, add `gosu` to apt, do the chown in entrypoint.sh against the *runtime* /data, then drop to the unprivileged `node` user via `exec gosu node:node agentmemory` before exec'ing the CLI. Symmetric to the iii-init busybox sidecar pattern in the repo-root docker-compose.yml. 3. Render's PORT injection broke the published port. Render auto-sets PORT (default 10000) and routes the public proxy to that port; the container then must bind 0.0.0.0:$PORT. Our container always binds 3111 (Dockerfile EXPOSE + iii-http config), so Render's proxy was forwarding to 10000 and getting connection refused. Fix: render.yaml now sets envVars PORT=3111 to override Render's default, plus exposes AGENTMEMORY_VERSION / III_VERSION as envVars (Render translates envVars into docker build args automatically). Also drops the AGENTMEMORY_REQUIRE_HTTPS / AGENTMEMORY_HMAC_FILE / AGENTMEMORY_DATA_DIR entries — server-side code never reads any of them; entrypoint reads HMAC_FILE / DATA_DIR via ${VAR:-default} so defaults already apply. 4. Coolify compose `ports:` bypassed Traefik. Per Coolify docs, `ports: ["3111:3111"]` binds the port directly on the host outside of the proxy network. The deploy would have been reachable on `http://<host>:3111` with no TLS termination and no domain routing. Fix: replaced `ports:` with `expose:` so the port is only reachable on the internal proxy network. Operator now sets the service domain in the Coolify UI as `https://<fqdn>:3111` (the `:3111` is Coolify's proxy hint, not a public port). Also added `SERVICE_FQDN_AGENTMEMORY_3111` to the environment so the container can know its public URL via Coolify's magic-env convention. Other minor fixes: - railway.json adds `requiredMountPath: /data` so Railway fails fast if the operator forgets to attach a volume. - fly.toml drops the dead-letter [env] block (server code does not read AGENTMEMORY_REQUIRE_HTTPS / AGENTMEMORY_HMAC_FILE / AGENTMEMORY_DATA_DIR; entrypoint defaults handle the latter two). - All 4 entrypoints unified — same script, same heredoc, copy with `cp -p` at build time. - READMEs updated to reflect the new bound port story, the manual Render Blueprint flow (Render's deploy-button auto-detection only scans repo-root render.yaml), the Coolify proxy / domain pattern, and the gosu privilege drop. Best-practice audit results (fly.toml, railway.json, render.yaml, docker-compose.yml) verified against current platform docs — fly.toml schema is clean, railway.json gets the requiredMountPath addition, render.yaml + coolify compose get the rewires above. * fix(deploy): pin iii-sdk to 0.11.2 via npm overrides agentmemory@0.9.12 declares `iii-sdk: ^0.11.2`, which `npm install` caret-resolves to **0.11.6** as of writing. That bumps the worker SDK ahead of the engine the agentmemory repo pins (iii v0.11.2). The two versions are wire-compatible in the calls agentmemory uses today, but the policy intent in the repo is single-version lockstep until the v0.11.6 sandbox-everything model is wired through — running SDK 0.11.6 against engine 0.11.2 in production silently re-introduces the very drift the AGENTMEMORY_III_VERSION pin exists to prevent. `npm install -g` ignores the `overrides` field, so we cannot fix this with the existing global install. Switch each Dockerfile to a local install under /opt/agentmemory with a one-line package.json carrying: overrides: { iii-sdk: 0.11.2 } then symlink the produced `node_modules/.bin/agentmemory` into `/usr/local/bin/agentmemory` so the entrypoint invocation stays identical. Verified locally that this resolves `node_modules/iii-sdk/package.json` to version 0.11.2 (was 0.11.6 without the override). Side effects: - Entrypoint III_CONFIG path moves from `/usr/local/lib/node_modules/@agentmemory/agentmemory/dist/iii-config.yaml` (global) to `/opt/agentmemory/node_modules/@agentmemory/agentmemory/dist/iii-config.yaml` (local). agentmemory CLI's findIiiConfig() resolves __dirname through the symlink to the local install so candidate-0 lookup still hits the file we overwrite. - New ARG `III_SDK_VERSION=0.11.2` declared in every Dockerfile, surfaced as a `build.args` field in coolify's compose and as an envVar in render.yaml so operators can pin a different SDK version if they manually migrate to engine 0.11.6+ down the line. - AGENTMEMORY_III_VERSION env now baked into the image (matches the engine pin) so the CLI's iii-binary download fallback path also resolves to the right version if PATH lookup ever misses. * fix(deploy): set TINI_SUBREAPER=1 so tini reaps zombies on Fly End-to-end deploy to fly.io surfaced the warning: [WARN tini (645)] Tini is not running as PID 1 and isn't registered as a child subreaper. Zombie processes will not be re-parented to Tini, so zombie reaping won't work. Fly's init system holds PID 1, so tini lands as a child process and the agentmemory CLI (which spawns iii detached) ends up with no process reaping its eventual zombies. Setting TINI_SUBREAPER=1 triggers prctl(PR_SET_CHILD_SUBREAPER, 1) so tini still reaps descendant zombies even when it isn't PID 1. Same fix applies to Railway and Render (which also wrap our entrypoint under their own init) and to Coolify when the operator runs the container under any PID-1-grabbing supervisor like systemd-nspawn. Verified end-to-end against fly.io: - App agentmemory-ghumare64 deployed via remote builder - 114 MB image (iii engine + node:22-slim + npm packages) - 1 GB volume in iad provisioned and mounted - First-boot HMAC generated and persisted to /data/.hmac - iii-engine + agentmemory worker came up clean - Worker registered with iii engine over ws://localhost:49134 - /agentmemory/livez returned 200 in 220 ms - Bearer auth gating verified: 401 without secret, 200/201 with it - POST /agentmemory/observe ingested a synthetic observation - POST /agentmemory/smart-search returned a valid response envelope Only outstanding log noise after this commit is a Node punycode deprecation warning from a transitive dep — not actionable from this side. * fix(deploy): backfill TINI_SUBREAPER=1 on railway + render Dockerfiles Previous commit missed these two — same reasoning applies (Railway and Render both wrap our entrypoint under their own platform init, so tini lands as a non-PID-1 child and zombie reaping needs the prctl subreaper bit set). * fix(deploy): apply live-fly-deploy findings across all templates Real end-to-end deploy on fly.io surfaced four refinements that generalise across the other three templates. This commit ports them. 1. Health-check grace_period: 10 s -> 30 s. Measured cold start on a fresh fly.io machine in `iad`: machine image prepared : 5.1 s volume mount + format : 2.5 s firecracker boot : 1.0 s entrypoint + chown : 0.5 s iii-engine ready : 3.0 s agentmemory worker reg : 2.0 s -------------------------------- healthcheck passes : ~9-10 s `grace_period = "10s"` (fly.toml) and `start_period: 20s` (coolify compose + Dockerfile HEALTHCHECK) were both within the noise of the measured time. Bumped both to 30 s — gives a 3x safety margin without affecting normal-operation health-check cadence. Railway and Render manage their own initial-deploy grace windows server-side, so railway.json's `healthcheckTimeout: 30` (per-attempt) and render.yaml's `healthCheckPath` stay as-is. 2. Documented LLM + embedding provider env-var menu in deploy/README.md. Live deploy logs surface a clear warning: [agentmemory] No LLM provider key found (ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENROUTER_API_KEY, MINIMAX_API_KEY). LLM-backed compression and summarization are DISABLED -- using no-op provider. The deploy READMEs never told operators how to opt in. Added a table covering ANTHROPIC / GEMINI / OPENROUTER for LLM, OPENAI / VOYAGE for embeddings, plus AGENTMEMORY_AUTO_COMPRESS and AGENTMEMORY_INJECT_CONTEXT for the corresponding behaviour flags. Each platform's variable-injection surface (flyctl secrets, dashboard Environment tab) is named in the same table. 3. Captured cold-start budget in deploy/README.md. Same step-by-step measured timing above is now in the shared README so operators have a concrete expectation rather than guessing why first-byte after `fly deploy` takes ten seconds. Notes that every template's start window is sized for 3x of this. 4. fly README: documented shared-IPv4 + dedicated-IPv6 default. Fly assigns one of each by default at no charge. Legacy clients without SNI that need a dedicated IPv4 can buy one for $2/month via `fly ips allocate-v4`. Added the build-time stats observed in the real deploy (~30 s first build, ~10 s cached rebuild, 114 MB image) to the Known Caveats section so operators sizing build-minute budgets have real numbers. No code paths changed -- only TOML / YAML / Markdown surface tweaks and one HEALTHCHECK `start_period` bump in the coolify Dockerfile. |
||
|
|
8c3418cc61 |
feat(search): CJK tokenizer for BM25 (#362)
* feat(search): CJK tokenizer for BM25 (closes #344) BM25 previously tokenized Chinese, Japanese, and Korean memories as a single sentence-long token because CJK scripts don't put spaces between words, leaving the keyword leg of hybrid search as dead weight for CJK users. Greek, Cyrillic, accented Latin already work since v0.9.12 (#327) because they split cleanly on whitespace. Detect CJK by Unicode block, then route per-run: - Han: @node-rs/jieba (native, no model download), Jieba.cut(text, hmm) - Kana (with mixed kanji): tiny-segmenter (pure JS, ~25KB) - Hangul: rule-based, each [가-]+ run is one token Both segmenters declared in optionalDependencies so the bundle stays small for users who don't need them. If the dep isn't installed the segmenter soft-falls to whole-run tokenization (the pre-fix behavior) and prints a one-time stderr hint per language. Non-CJK input goes through the existing tokenize path unchanged — zero behavior change for the Latin/Greek/Cyrillic/Hebrew/Arabic paths. New fixtures in test/search-index.test.ts cover Chinese (项目记忆存储), Japanese (プロジェクト記憶), and Korean (프로젝트 메모리 저장소). Existing Greek + ASCII tests stay green. Suite grew 899 -> 902 tests. * fix(cjk): preserve source order across mixed CJK + non-CJK runs The previous segmentCjk walked the input three times — once for Han/kana runs, once for Hangul runs, once for non-CJK pieces — and concatenated the results. For a mixed string like "abc 메모리 def 项目 ghi" that emitted ["메모리","项目","abc","def","ghi"], dropping the original positional information BM25 phrase queries and any downstream consumer keyed on token order rely on. Replace with a single left-to-right pass over CJK_RUN_RE: emit any intervening non-CJK substring (trimmed), then route the matched CJK run to segmentHangul / segmentKana / segmentHan based on which script it contains, and finally emit any trailing non-CJK piece. Added a regression test asserting source order across four mixed-script strings. Drops the now-unused HAN_KANA_RUN_RE / HANGUL_RUN_RE regex constants. |
||
|
|
f365d38c75 |
docs(python): iii-sdk example replaces standalone REST client (#364)
Closes #342. agentmemory functions register as mem::remember, mem::observe, mem::context, mem::smart-search, mem::forget via sdk.registerFunction. iii-sdk already ships in Python, Rust, and Node — any of them can call those functions directly over the iii WebSocket transport. A standalone Python REST client would duplicate the transport layer and fragment the cross-language story. This patch adds: - examples/python/quickstart.py — minimal save + smart-search loop. - examples/python/observe_and_recall.py — observation ingest + context rendering at a token budget. - examples/python/README.md — function map + install + usage. - README "Programmatic access (Python / Rust / Node)" section pointing at the example and the three SDK install commands. REST on :3111 is unchanged for hosts without an iii runtime. |
||
|
|
d1517465ca |
feat(bench): load-100k.ts harness with p50/p90/p99 output (#363)
Adds a reproducible, dependency-free load harness so we can answer "what's p99 at 100k memories under concurrency 100?" with a number instead of a shrug. The harness seeds N synthetic memories against a local agentmemory daemon (defaults to http://localhost:3111, optional autostart via AGENTMEMORY_BENCH_AUTOSTART=1), then drives a matrix of (N, concurrency, endpoint) cells with hand-rolled Promise.allSettled batches. Per-request latency is collected via performance.now() and summarized as nearest-rank p50 / p90 / p99 plus min / max / errors and wall-clock throughput. Results are written to benchmark/results/load-100k-<short-git-sha>.json with a schema_version field so future format changes don't silently break consumers. Defaults match issue #346: N in {1000, 10000, 100000} x C in {1, 10, 100} x three endpoints (POST /agentmemory/remember, POST /agentmemory/smart-search, GET /agentmemory/memories?latest=true). Each cell issues BENCH_OPS=200 requests by default — enough samples for stable p99 without dragging a 100k-seed run past tens of minutes. Content is generated by a small noun/verb/concept vocabulary fed by a mulberry32(BENCH_SEED) PRNG so re-running the harness against the same daemon build yields the same seed corpus. Reproducibility, not realism, is the point — latency variance comes from the daemon, not JSON payload jitter. Files: - benchmark/load-100k.ts: main harness - benchmark/lib/percentiles.ts: zero-dep pXX helper, nearest-rank - benchmark/README.md: how to run, what gets measured, where results land, and why p99 is the number you want for capacity planning - benchmark/results/load-100k-96c0ed0.json: example result from a small-N (N=1000, C=10) verification run against a fresh daemon - package.json: wires `npm run bench:load` - CHANGELOG.md: Unreleased entry + a Performance section placeholder describing where per-release numbers should land going forward Verified locally at BENCH_N=1000 BENCH_C=10 BENCH_OPS=200 — three cells, zero errors, JSON written. Full 100k matrix is intentionally deferred to CI/release time. Closes #346. |
||
|
|
96c0ed017d |
chore(security): add dependabot config + close 14 open advisories (#348)
- Add .github/dependabot.yml covering npm (root, website, 3 integrations) + github-actions. Weekly cadence, grouped minor/patch, scoped commit prefixes. - Bump website next ^16.2.4 → ^16.2.6: closes 13 open advisories (middleware/proxy bypass, SSRF, DoS, CSP-nonce XSS, image-opt DoS, cache poisoning, beforeInteractive XSS, segment-prefetch routes). - Pin website postcss → ^8.5.10 via overrides: closes XSS via unescaped </style> in CSS stringify output (resolves to 8.5.14). Verified: - `npm audit --omit=dev` → 0 vulnerabilities. - `npm run build` clean on Next 16.2.6 (Turbopack). |
||
|
|
87fae50b79 |
chore(release): v0.9.12 — BM25 unicode + vector live-write + viewer hardening (#337)
Three landed PRs since v0.9.11: - #327 (#295) — BM25 tokenizer now accepts non-ASCII (Greek, accented Latin, Hebrew, Arabic, Cyrillic), VectorIndex.add now actually called at runtime via vectorIndexAddGuarded helper with dim guard + input clip, migrateVectorIndex for dim migrations. - #326 (#277) — RetentionScore type no longer declares source twice; JSDoc back-compat note no longer shadowed. - #335 (#323) — viewer drops Google Fonts <link> (CSP-blocked), loadDashboard now surfaces load errors inline instead of sticking on "Loading dashboard…". Bumping 0.9.11 -> 0.9.12 across the 9 standard files: - package.json - packages/mcp/package.json - plugin/.claude-plugin/plugin.json - plugin/.codex-plugin/plugin.json - src/version.ts - src/types.ts (ExportData.version literal) - src/functions/export-import.ts (supportedVersions) - test/export-import.test.ts (round-trip expectation) - CHANGELOG.md (new 0.9.12 entry) 886 / 886 tests pass. Build clean.v0.9.12 |
||
|
|
1a706ca2d6 |
fix(integrations): warn when bearer auth crosses plaintext HTTP to non-loopback (#315)
* fix(integrations): warn when bearer auth crosses plaintext HTTP to non-loopback (#275) Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> * test(integrations/315): edge cases for IPv6 loopback + LAN IPs + lookalike hostnames + docs @mvanhorn's PR #315 lands the right plaintext-bearer-auth warn-once pattern across three runtimes (hermes/python, openclaw/mjs, pi/typescript). Reviewed cleanly. Adding test cases + docs before merge so the contract is locked: Test additions (test/integration-plaintext-http.test.ts pi suite, +4 cases, 13 total in suite): - IPv6 loopback (http://[::1]:3111) — URL parser strips brackets, hostname comes back as `::1`, the LOOPBACK_HOSTS set already covers this. Test pins the parsing semantic so a future refactor can't silently break it. - Private LAN IPs (RFC1918 ranges 192.168.x.y, 10.x.x.x) — NOT loopback, guard MUST warn. RFC1918 was a deliberate design choice (loopback set stays minimal: localhost / 127.0.0.1 / ::1). LAN deployment behind a tunnel still leaks the bearer over the wire. - No-secret short-circuit (guard with secret="" or undefined) — guard never fires when a bearer wouldn't actually be sent. Mirrors the usesPlaintextBearerAuth() early-return. - Lookalike hostname (http://localhost.evil.com:3111) — `parsed.hostname` returns the full `localhost.evil.com` string, which is NOT in LOOPBACK_HOSTS. Guard warns. This pins the exact-match (vs prefix) loopback semantic. Docs additions: - integrations/pi/README.md and integrations/hermes/README.md now document AGENTMEMORY_REQUIRE_HTTPS in the env-vars table — same row shape as AGENTMEMORY_SECRET, names the env value (=1), describes the warn-vs-throw semantic, and lists the loopback hostnames the guard recognises. No code changes to the guard itself — it's already correct. Pre-merge hardening only. 899 / 899 tests pass on rebased branch. Build clean. --------- Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Co-authored-by: Rohit Ghumare <ghumare64@gmail.com> |
||
|
|
24f6c5fb24 |
fix(search): BM25 unicode + vector index live-write — improved (closes #295, supersedes #296) (#327)
* fix #2: BM25 tokenizer now preserves Cyrillic/Unicode via \p{L}\p{N} regex Signed-off-by: Nikita Nosov <20nik.nosov21@gmail.com> * fix #1: add getVectorIndex/setVectorIndex + vector embedding in rebuildIndex Signed-off-by: Nikita Nosov <20nik.nosov21@gmail.com> * fix #1+#3: vectorIndex.add in remember/observe/compress + setVectorIndex in index.ts Signed-off-by: Nikita Nosov <20nik.nosov21@gmail.com> * fix: add migration script, Cyrillic BM25 tests, vector-populate tests, rebuildIndex vector test Adds migrateVectorIndex utility for re-embedding when embedding provider dimensions change. Adds comprehensive test coverage for Cyrillic tokenization, vector index population on remember/rebuildIndex, and migration edge cases. Signed-off-by: Nikita Nosov <20nik.nosov21@gmail.com> * chore: remove internal research spec from PR branch Signed-off-by: Nikita Nosov <20nik.nosov21@gmail.com> * refactor(search): centralize vector-index writes via vectorIndexAddGuarded Pre-merge hardening on top of @nik1t7n's PR #296. Original PR had vectorIndex.add() inlined at four sites (remember, observe, compress, rebuildIndex) with the same try/catch shape repeated each time. That open-codes three sharp edges per site: 1. Missing dimension guard at the write boundary. Per #248, persistence load already refuses to start when persisted vectors mismatch the active provider's dimensions — but the live-write path had no symmetric check. A mis-configured embedding provider returning a different-length Float32Array would silently corrupt the in-memory index until next restart cleared it. 2. No input clipping. Memory.content can be arbitrarily large (the MemorySlot path even allows 20KB content per slot). Most embedding providers cap input around 8k tokens and 400 on overflow. A single oversize memory could throw at the provider and lose the vector entry; worse, on local-Ollama-style providers it can cost real inference time. 3. Inconsistent error logging (rebuildIndex had `catch {}` swallowing the error entirely, the other three sites logged via logger.warn with slightly different shape). Consolidates into a single exported `vectorIndexAddGuarded()` in src/functions/search.ts: - Reads vectorIndex + currentEmbeddingProvider from module-level state (same singletons the PR added). - Clips embed input to 16k chars (EMBED_MAX_CHARS) — well under every provider's documented limit, leaves headroom for chars-per-token variance across languages. - Validates embedding.length === provider.dimensions before calling vi.add(); logs structured warning and skips on mismatch. - Single logger.warn on embed failure with provider name + error message + indexed-item kind ("memory" | "observation" | "synthetic") + id. - Returns boolean (true on success) so callers can branch if they want, but every existing call site fire-and-forgets — the contract is "soft-fail: index entry skipped, upstream save still succeeds". Rewrites the four call sites (remember.ts, observe.ts synthetic branch, compress.ts post-LLM, search.ts rebuildIndex memories + obs loops) to call vectorIndexAddGuarded() instead of the inline try/catch. Side effect: cleans up the lingering `catch {}` in rebuildIndex that was swallowing rebuild-time embed failures silently — every failure now surfaces in logs at a consistent shape. Tests: - test/multimodal.test.ts mocked `../src/functions/search.js` and exposed only getSearchIndex; needed vectorIndexAddGuarded added to the mock surface or test fails with "No vectorIndexAddGuarded export is defined on the search.js mock". Added the export to the mock. - All other tests (search-index Cyrillic, search rebuildIndex vector, vector-index-populate, migrate-vector-index) green on the helper refactor — no behavioural change beyond the three guards above. 886 / 886 tests pass. Build clean. Out-of-scope (deferred to follow-ups): - Blocking await on every save. The embed call is on the request hot path. For local Ollama / on-device providers this is fine; for network providers it adds 200ms-2s latency per remember/observe. Should be async / fire-and-forget with a write-behind queue — separate perf PR after this lands. - Periodic persistence flush of the vector index. Today the index is serialized at process exit (per IndexPersistence). Runtime adds between flushes survive in memory but die on crash. Acceptable for this PR; future PR can add a debounced flush. - Batching in rebuildIndex. Today the rebuild path embeds one doc at a time. embedBatch() exists on every provider and would be ~5-10x faster for cold restarts on large corpora. Defer. * fix(search): tighter migrate guard + rebuild vector clear + test hygiene Five reviewer findings applied. Five skipped with reason: APPLIED: 1. migrate-vector-index.ts filter now requires m.content (and rejects empty/whitespace). Without this, undefined m.content would concat to "title undefined" and poison the embed input. Memory.content is declared string but #277-era data shapes had cases where it could be missing — defensive filter. 2. migrate-vector-index.ts success expression now derives from the failed counter (success: failed === 0). Previously hardcoded true so callers couldn't rely on the boolean alone. 3. search.ts rebuildIndex now calls vectorIndex?.clear() before the repopulation loops. Symmetric to the BM25 idx.clear() that was already there. Without this, memories deleted between runs leave orphan embeddings in the vector store after every rebuild. 4. test/vector-index-dimensions.test.ts assertion tightened from toBeGreaterThan(0) to toBe(1) for both totalProcessed and vectorSize. The seeded fixture has exactly one observation; loose assertion would hide regressions where the migration produced extra/duplicate vectors. 5. test/vector-index-populate.test.ts switched to beforeEach/afterEach for the setVectorIndex / setEmbeddingProvider lifecycle. Manual cleanup in each test body was easy to drift on additions. PLUS: 6. compress.ts BM25 add now wrapped in try/catch matching the remember.ts pattern for parity. Helps cross-file symmetry even though SearchIndex.add doesn't throw on valid input. SKIPPED (with reason): - Wrapping vectorIndexAddGuarded calls in additional try/catch at remember.ts / observe.ts / compress.ts: the helper has internal try/catch (src/functions/search.ts:67-89). It NEVER throws. Adding outer try/catch is dead code. The reviewer missed the helper's contract. - vector-index-dimensions.test.ts mockKV.delete -> real deletion: the tests in that file don't exercise deletion semantics, and migrateVectorIndex itself never calls kv.delete. Touching it would add noise without effect on any assertion. - recordAudit + Promise.allSettled in remember.ts: out of scope. recordAudit is already called earlier in the function. Parallelizing cascade-update with vector-add changes failure semantics (cascade- update is currently fire-and-forget) — separate perf PR if wanted. - validateDimensions(N) assertion in vector-index-dimensions.test.ts: vectorSize is the COUNT of stored vectors, not their dimensions. Adding a validateDimensions check on the result would require exposing the new index. Out of scope for assertion tightening. 886 / 886 tests pass. Build clean. * fix(migrate): per-session isolation + dim guard + drop unused oldIndex param Four reviewer findings applied. One skipped with reason: APPLIED: 1. Per-session isolation in migrateVectorIndex observations phase. The previous shape wrapped the entire sessions for-loop in one try/catch — a single bad session (kv.list throws, embedBatch rejects, etc.) aborted every subsequent session and silently truncated the migration. The loop now has per-session try/catch; failures increment `failed`, append the session id to a new `failedSessions` array on the result, and the loop continues. The kv.list<sessions> call itself is now guarded separately — if THAT fails the whole migration aborts (no sessions to iterate), but with `failed` recorded and `failedSessions` empty so the caller can distinguish. 2. Drop `oldIndex` parameter from migrateVectorIndex signature. It was dead — no read path inside the function. Three test call sites updated to match the new signature migrateVectorIndex(kv, newProvider). Confused the API contract; if we ever DO need a previous-index argument it can come back with a real use case. 3. Dim guard on every embedding result inside migrateVectorIndex, in both the memories phase and the per-session observations phase. Extracted into a local isValidEmbedding() helper that mirrors the shape of search.ts::vectorIndexAddGuarded's guard: log structured warn + skip on mismatch. Without this, a misconfigured provider returning the wrong-length Float32Array would corrupt the rebuilt index, defeating the #248 persistence-load guard. 4. Rename `vi` -> `vectorIndex` in test/vector-index-populate.test.ts so the let binding doesn't shadow the vitest `vi` import. Practical impact: future tests in that file can use `vi.fn()` / `vi.spyOn()` without renaming this binding first. Cosmetic but cheap. SKIPPED (with reason): - Use embedBatch in rebuildIndex (search.ts): perf optimization, not correctness. Already called out as deferred in the previous review's commit message. Cold-restart speed matters but the fix here is bigger refactor (collect-into-batches with metadata, validate each, add only successful) and worth its own PR. Will pick up when we also do the write-behind queue for live-write batching. Result envelope: Added `failedSessions: string[]` to MigrateVectorIndexResult so the caller can decide whether to retry per-session or just abort. Existing fields (success, totalProcessed, failed, vectorSize) unchanged in shape. 886 / 886 tests pass. Build clean. * fix(migrate): attribute batch-level failures to correct count + distinguish list-fail Two new reviewer findings on migrate-vector-index. Six earlier findings re-flagged in the same review block had already been addressed in prior commits on this branch (verified inline): - compress.ts BM25 try/catch (commit |
||
|
|
117f49a52a |
fix(viewer): drop Google Fonts link + surface dashboard load errors (closes #323) (#335)
@hem57 reported the viewer dashboard tab stuck on "Loading dashboard..." on Windows under v0.9.11 with CSP violations visible in the browser console. Two narrow fixes that together close the bug premise: 1. Drop the <link rel="stylesheet" href="https://fonts.googleapis.com/ css2?..."> tag. The viewer ships a strict CSP (src/auth.ts:16-30): default-src 'none' style-src 'unsafe-inline' font-src 'self' Note that style-src does NOT include the Google Fonts origin and font-src does NOT include fonts.gstatic.com. Per CSP semantics style-src does not fall back to default-src, so the external stylesheet was blocked outright. On every viewer load the browser logged a CSP violation for the <link>, plus more for each blocked font file. On Windows Edge this produced the screenshot @hem57 shared. The viewer's font stack already declares system fallbacks on every --font-* CSS variable (src/viewer/index.html:39-42): --font-display: 'Playfair Display', Georgia, ... --font-body: 'Lora', Georgia, serif; --font-ui: 'Inter', -apple-system, sans-serif; --font-mono: 'JetBrains Mono', 'SF Mono', ... Dropping the <link> falls back to the system stack with no visual loss on platforms that ship Georgia / SF Mono / system serifs (most macOS, Windows, Linux distros). 2. Wrap loadDashboard() body in try/catch that surfaces failures in the dashboard panel. Before this fix, any uncaught error in the Promise.all of apiGet calls (or in renderDashboard) left the dashboard stuck on the "Loading dashboard..." text forever, with no indication of what went wrong. apiGet() already swallows network/HTTP errors and returns null, so most errors are caught upstream — but renderDashboard can still throw on shape surprises (CSP-blocked event handler binding, undefined object access, etc.). The new catch renders an inline error block with the error message and a pointer to the browser console for full detail. Future reports of "stuck Loading" will at least come with a concrete error message instead of just a screenshot of the placeholder. Out-of-scope (deferred to follow-up): - CSP script-src-attr 'none' blocks inline event handlers in innerHTML-injected dynamic markup (oninput= / onchange= at lines 2706, 2765, 2766, 2852). Browser logs a CSP violation per element but the handlers simply don't bind — the dashboard still loads. Eliminating these requires moving from inline strings to delegated event listeners, ~50 lines of refactor across four search/filter controls. Separate PR. - Self-hosting the fonts (instead of dropping them) would preserve the design intent but adds 200-400KB of font files to the dist. Acceptable tradeoff for now: fonts go, system stack is fine. Test plan: - npm test — 877/877 pass. - npm run build — tsdown clean, dist/viewer/index.html regenerated. - Visual sanity check pending: open localhost:3113 in Chrome dev tools, confirm no CSP violations from font-related requests; force a 502 from one apiGet endpoint and confirm dashboard shows the error message instead of sticking. |
||
|
|
cba234d950 |
fix(types): remove duplicate source field in RetentionScore (closes #277) (#326)
cl0ckt0wer reported in #277 that RetentionScore declared source twice (src/types.ts:835 and :842), both as ?"episodic" | "semantic". TS silently accepts duplicate property declarations when types are identical; runtime treats the second as the canonical declaration, which means the documented JSDoc on the FIRST one (referring to #124, pre-0.8.10 rows, scope probing semantics) was effectively dead code to anyone reading the type definition. Verified across src/ that no code path WRITES to source twice: grep returned zero callers setting source on RetentionScore, so removing the duplicate is a no-op at runtime. The kept copy retains the #124-referencing JSDoc so callers still see the back-compat note. Test impact: none (type-only). Full suite 877/877 unchanged. |
||
|
|
25dddc4379 |
chore(release): v0.9.11 — Codex plugin platform + OpenClaw slot fix + website star button (#317)
Three landed PRs since v0.9.10: - #311 — Codex plugin support (manifest + marketplace + Codex-shaped hooks subset, sharing the same scripts as the Claude Code plugin via CLAUDE_PLUGIN_ROOT OOTB-compat injection on Codex's hook engine) - #310 — OpenClaw plugins.slots.memory now actually claimed via api.registerMemoryCapability({ promptBuilder }); older builds without the capability API still load via the existing hook-only path - #316 — Star on GitHub button in the website hero CTA row, live stargazer count via api.github.com, 30-minute localStorage cache, graceful degrade on API failure Bumping 0.9.10 -> 0.9.11 across the 8 standard files: - package.json - packages/mcp/package.json - plugin/.claude-plugin/plugin.json - plugin/.codex-plugin/plugin.json - src/version.ts - src/types.ts (ExportData.version literal) - src/functions/export-import.ts (supportedVersions) - test/export-import.test.ts (round-trip expectation) - CHANGELOG.md (new 0.9.11 entry) 877 / 877 tests pass. Build clean.v0.9.11 |
||
|
|
3f661d2e42 |
feat(website): add Star on GitHub button to hero CTA row (#316)
Hero only had START IN 60 SECONDS + SEE IT MOVE. Missing the obvious
CTA for visitors who landed via X/Reddit and want to bookmark before
installing.
Adds GitHubStarButton component next to the existing ghost button:
- ghost-style button matching the existing visual register
(no new design surface introduced)
- star icon (inline SVG, currentColor) + STAR label + live
star count
- count fetches once on mount from api.github.com/repos/<repo>
(unauthenticated, 60 req/h limit per IP — fine for a marketing page)
- 30-minute localStorage cache keyed by repo so repeat visits don't
re-hit the API
- graceful degradation: if fetch fails (offline, rate-limited,
blocked), button still renders without the count
Only client-side ("use client") because we want the live count.
No deps added. Build clean (Next 16 static export).
|
||
|
|
48b11e8351 |
fix(openclaw): claim plugins.slots.memory via registerMemoryCapability (#310)
* fix(openclaw): claim plugins.slots.memory via registerMemoryCapability
The OpenClaw integration declared kind: "memory" in its manifest and
wired before_agent_start / agent_end hooks, but never called
api.registerMemoryCapability(...). On OpenClaw 2026.4.9+ that
left plugins.slots.memory = "agentmemory" reporting unavailable
even though the hooks worked end-to-end and the REST API was
healthy.
Per OpenClaw's plugin SDK declarations
(plugin-sdk/src/plugins/types.d.ts), api.registerMemoryCapability
takes a MemoryPluginCapability and is the canonical way to occupy
the memory slot. All capability fields are optional. The
promptBuilder field has signature:
(params: { availableTools: Set<string>; citationsMode? }) => string[]
This commit:
- Calls api.registerMemoryCapability({ promptBuilder }) at register
time when the host exposes it. promptBuilder returns three lines
describing agentmemory as the active memory provider, including
the configured base_url so the agent knows where recall is
coming from.
- Guards on `typeof api.registerMemoryCapability === "function"`
so older OpenClaw builds (pre-capability API) still load via
the existing hook-only path. Net behaviour on older hosts is
unchanged.
- Does NOT register a runtime adapter. OpenClaw's current
MemoryRuntimeBackendConfig type is exactly { backend: "builtin" }
or { backend: "qmd"; qmd?: { command?: string } }; both are
in-process backends and don't fit an external REST service.
The hook-driven recall + capture flow is the working
integration path; runtime registration is documented as a
follow-up gated on an upstream OpenClaw change to add an
"external" backend variant.
README troubleshooting block adds a specific entry for the
"unavailable slot" symptom, pointing at the registerMemoryCapability
fix. The README also documents the runtime-adapter scope decision
so users running deep host integrations aren't surprised.
3 new tests in test/openclaw-plugin.test.ts assert:
1. registerMemoryCapability is called with a promptBuilder when
the host supports it.
2. Plugin still registers hooks and does not throw on older hosts
that don't expose registerMemoryCapability.
3. promptBuilder respects the configured base_url.
871 / 871 tests pass.
Verified against the openclaw npm package surface:
- api.registerMemoryCapability is declared at
plugin-sdk/src/plugins/types.d.ts:2028
- MemoryPluginCapability is declared at
plugin-sdk/src/plugins/memory-state.d.ts
- promptBuilder signature matches MemoryPromptSectionBuilder
- No import from openclaw/extensions/* needed (and that path is
not in the package's exports map anyway)
This is a fresh implementation. The closed PR #302 attempted the
same end goal but imported memoryRuntime from a non-existent
path (openclaw/extensions/memory-core/runtime-api) and would have
failed with ERR_PACKAGE_PATH_NOT_EXPORTED at load time.
* fix(openclaw): accept params arg in promptBuilder to match MemoryPromptSectionBuilder
Reviewer flagged that promptBuilder was declared as () => [...] which
doesn't match the MemoryPluginCapability contract verified from
openclaw@2026.5.7 (plugin-sdk/src/plugins/memory-state.d.ts):
type MemoryPromptSectionBuilder = (params: {
availableTools: Set<string>;
citationsMode?: MemoryCitationsMode;
}) => string[];
JavaScript would tolerate the zero-arg form at runtime (extra args
are ignored), but the signature was misleading and would fail a
TypeScript port. Updated to (_params) => [...] with a comment
explaining we accept-but-ignore. Underscore-prefix conveys
intentional non-use to lint/readers.
Validation: 3/3 openclaw tests still pass (they invoke
promptBuilder({ availableTools: new Set() }) — both old and new
signatures accept that call).
|
||
|
|
c21410e287 |
feat(plugin): ship Codex plugin manifest + marketplace (#311)
* feat(plugin): ship Codex plugin manifest + marketplace (#codex-plugins)
OpenAI Codex shipped a plugin platform
(developers.openai.com/codex/plugins) with the same shape as Anthropic
Claude Code plugins: .codex-plugin/plugin.json manifest, optional
.mcp.json, hooks/hooks.json, and a skills/ directory.
Verified against openai/codex source
(codex-rs/hooks/src/engine/discovery.rs): Codex's hook engine
explicitly injects CLAUDE_PLUGIN_ROOT into hook subprocesses for
OOTB compat with existing Claude Code plugins. The wire-format
input schemas
(codex-rs/hooks/schema/generated/session-start.command.input.schema.json
and siblings) use the same field names as Claude Code
(session_id, cwd, hook_event_name, source, transcript_path, model,
permission_mode).
Net result: our existing plugin/ directory already works as a Codex
plugin with two small additions:
1. plugin/.codex-plugin/plugin.json — Codex manifest pointing at the
shared ./.mcp.json, ./skills/, and a Codex-specific ./hooks/
hooks.codex.json. Name (kebab-case), version, description match
the Claude Code manifest.
2. plugin/hooks/hooks.codex.json — Codex-compatible hook subset.
Drops SubagentStart, SubagentStop, SessionEnd, Notification,
TaskCompleted, PostToolUseFailure (Claude-Code-only). Keeps
SessionStart, UserPromptSubmit, PreToolUse, PostToolUse,
PreCompact, Stop. Adds Codex-specific `statusMessage` decorations
on the two hooks the user sees most (SessionStart + UserPromptSubmit).
The script commands themselves still reference ${CLAUDE_PLUGIN_ROOT}
because Codex's engine injects it.
3. .codex-plugin/marketplace.json at repo root — git-subdir source
pointing at ./plugin so `codex plugin marketplace add rohitg00/
agentmemory` works the same way `claude plugin marketplace add`
does. Mirrors the existing .claude-plugin/marketplace.json shape
but in Codex's marketplace schema (name + interface.displayName
+ plugins[].source.{source, url, path, ref} + plugins[].policy).
README updates:
- Codex CLI tile in the Works-with-every-agent grid now reads
"6 hooks + MCP + skills" instead of "MCP server" to surface the
upgraded surface.
- "Other agents" install table now distinguishes "Codex CLI (MCP
only)" (existing codex mcp add path) from "Codex CLI (full
plugin)" (new marketplace install).
- New Codex section in the "paste this prompt" block with the
two-command install (server + marketplace add + plugin install),
the list of registered surfaces, and the env-var-injection note
citing the Codex source line.
6 new tests in test/codex-plugin.test.ts cover:
1. .codex-plugin/plugin.json present with kebab-case name and
required references.
2. Manifest version matches main package.json (so future bumps
don't drift the Codex side).
3. Every path referenced in the manifest resolves to a real file or
directory on disk.
4. hooks.codex.json contains only events Codex supports — any
future addition has to be added to a Codex allowlist or the
test fails loudly.
5. Every ${CLAUDE_PLUGIN_ROOT}/scripts/* command references an
existing script file (catches typos before users hit them).
6. .codex-plugin/marketplace.json declares git-subdir source with
path: "./plugin" pointing at this repo.
874 / 874 tests pass.
* fix(plugin/codex): align manifest description + assert PreCompact hook
Two reviewer findings addressed:
1. plugin/.codex-plugin/plugin.json's description claimed "12 hooks"
but Codex only registers 6 (the Codex hook input schemas don't
define SubagentStart, SubagentStop, SessionEnd, Notification,
TaskCompleted, PostToolUseFailure). Updated the description to
"6 hooks, 51 MCP tools, 4 skills, real-time viewer." so the
marketplace listing matches the registered surface.
2. test/codex-plugin.test.ts asserted five lifecycle events
(SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop)
but omitted PreCompact, leaving a regression hole: dropping
PreCompact from hooks.codex.json would still pass the test
because the allowlist loop only blocks UNKNOWN events. Added
expect(events).toContain("PreCompact") so the suite enforces
the full six-hook contract.
Skipped (with reason):
3. marketplace.json source.ref currently "main" — reviewer asked
for a fixed tag/SHA. Skipping because pinning to a tag forces
updating marketplace.json on every release; "main" matches our
convention where releases tag from main HEAD and pre-release
work is on feature branches. Users wanting an immutable cut
can pass --ref vX.Y.Z to `codex plugin marketplace add` per
Codex docs.
Validation: 6/6 codex tests pass; 874/874 full suite unchanged.
|
||
|
|
292e9f6af1 |
docs(readme): add Trendshift badge + star history graph + tagline fix (#305)
* docs(readme): add Trendshift badge + star history graph + tagline fix Three small README polishes flagged on the live README screenshot: 1. Add the Trendshift badge (repo #25123) directly under the tagline so the trending signal sits at eye level with the banner. 2. Add the star-history.com graph under the badge so the growth curve is visible without scrolling — uses <picture> with dark/light variants matching the rest of the README's prefers-color-scheme convention. 3. Tagline missed two first-party integrations that already ship plugin folders in this repo (integrations/hermes, integrations/openclaw); added Hermes + OpenClaw to the agent list so the supported-clients sentence matches what the integration index actually advertises. 4. Reflow the Karpathy-gist sentence onto a single italic line — the previous block-strong + <br/> shape wrapped awkwardly on narrow viewports. Not in this PR (deferred): the stat-tag row redesign flagged on the same screenshot. That needs new SVG assets in assets/tags/ and is worth doing as a separate visual pass rather than a quick edit. * Update README.md * docs(readme): simplify star history to plain img link GitHub's image proxy doesn't reliably render <picture> with external SVG sources from api.star-history.com — the entire block came back empty in rendered README. Switched to the standard plain img inside an anchor pattern that every other widely-used repo (k8s, react, deno, etc) uses for star-history embeds. Single URL, no prefers-color-scheme variants, single fetch path for the camo proxy. * docs(readme): switch star history to markdown form (& escaping bug) HTML form had unescaped `&type=Date` in src attribute. Browser/GitHub parsed `&type` as an HTML entity attempt, so the URL camo fetched was truncated to `?repos=rohitg00/agentmemory` — invalid endpoint, broken image. Markdown form auto-escapes ampersands, matching every other repo's star-history embed. * docs(readme): use canonical star-history chart endpoint with theme variants User-provided exact snippet. Switches: - /svg -> /chart endpoint (canonical star-history.com API) - type=Date -> type=date (lowercase, matches docs) - adds legend=top-left - <picture> with prefers-color-scheme dark/light sources - outer link uses query-param form (?repos=...) not fragment (#repos=...) Matches the pattern star-history.com's own embed-generator hands out.v0.9.10 |
||
|
|
5ea01c162c |
fix(deploy): distroless volume perms + viewer proxy + budget loop (#299, #301) (#304)
@flamerged reported three issues on a real production deployment: 1. (#301) v0.9.7's working-directory fix moved iii-config paths from ./data/... to /data/... so the named volume mount is actually reached. But iiidev/iii is distroless and runs as UID 65532, while `docker volume create` initializes the named volume mountpoint as root:root mode 755. Engine writes fail Permission denied (os error 13), the API silently buffers in RAM, every API call returns success, and state evaporates on every container restart — exactly what 0.9.7 set out to fix. Fix: ship a one-shot iii-init service in docker-compose.yml (busybox:1.36, ~4MB, exits in <100ms) that chowns /data to 65532:65532. iii-engine now has user: "65532:65532" and depends_on.iii-init.condition: service_completed_successfully. Verified live: pre-fix volume stayed 4.0K after API writes; post- fix volume grows to 44K with state_store.db/mem%3A*.bin files written through the named volume. 2. (#299) src/viewer/index.html ports detection hardcoded ':3113' as the fallback when window.location.port is empty (page served on 80/443 behind a reverse proxy). Every browser-side /agentmemory/* fetch went to <host>:3113, which is typically loopback-only on the self-hosted shape — the dashboard rendered cleanly but every panel showed the empty "first run" state. Fix: when neither ?port=N nor window.location.port is set, use window.location.origin as the REST base and window.location.host for the WebSocket URL — same-origin path works for both REST and live updates. Explicit ?port=N / non-default window.location.port paths unchanged. 3. (bundled) mem::context budget loop used `break` on first oversized block. With #288's new pinned-slot injection sorting first via recency: Date.now(), one fat pinned slot could starve every smaller block downstream that would have fit. Switched to `continue` so smaller blocks still slip into remaining budget. Total tokens still bounded by tokenBudget; only composition under contention changes. Bumping 0.9.9 -> 0.9.10 across the 8 standard files (package.json, packages/mcp/package.json, plugin/.claude-plugin/plugin.json, src/version.ts, src/types.ts ExportData literal, src/functions/export-import.ts supportedVersions, the export round-trip test expectation, and CHANGELOG.md). 868 / 868 tests pass. Build clean. Volume + viewer fixes verified end-to-end live. |
||
|
|
03fb42db39 |
chore(release): v0.9.9 — pinned slot injection + MiniMax env loader (#290)
Two field-reported fixes landed since v0.9.8: - #288 (closes #286) — wires pinned memory slots into mem::context so agentmemory pinned-slot content actually reaches SessionStart. The renderPinnedContext / listPinnedSlots helpers from #182 had zero callers; this lands the wiring behind isSlotsEnabled(). - #289 (closes #285) — MiniMax provider now reads MINIMAX_BASE_URL via the shared getEnvVar() loader (so ~/.agentmemory/.env values get picked up), and the default endpoint is bumped from the stale api.minimaxi.com to api.minimax.io/anthropic per MiniMax's current Anthropic-compatible docs. Bumping 0.9.8 -> 0.9.9 across the 8 standard files (package.json, packages/mcp/package.json, plugin/.claude-plugin/plugin.json, src/version.ts, src/types.ts ExportData literal, src/functions/export-import.ts supportedVersions, the export round-trip test expectation, and CHANGELOG.md). 868 / 868 tests pass.v0.9.9 |