Files
Abdullah Alaqeel 8c90741c63 chore(deps): migrate @xenova/transformers to @huggingface/transformers v4 (#1096)
* chore(deps): migrate @xenova/transformers to @huggingface/transformers v4

@xenova/transformers@2.x is deprecated and silently broken on Node 22+
(see #479). The project was renamed to @huggingface/transformers; same
Apache-2.0 license, same code. v4 ships onnxruntime-node/web and sharp
as hard deps, so they're dropped from our optionalDependencies.

Pipeline / RawImage.fromBlob / tolist / text-classification output
shape all unchanged. Three behavior-preserving adjustments needed:

- All 4 pipeline call sites pass { dtype: "q8" }. v4's default on Node
  is fp32 (DEFAULT_DEVICE_DTYPE = "fp32"); v2 defaulted to quantized=true.
  Without explicit dtype, all 4 sites silently regress (~3.5x larger
  download, slower inference). dtype "q8" maps to model_quantized.onnx
  per v4's DEFAULT_DTYPE_SUFFIX_MAPPING; file exists in all 3 Xenova
  models. This was the regression that prompted the test additions below.
- src/providers/embedding/local.ts: split import try/catch from
  pipeline() call so model-load errors (network, missing q8 variant,
  disk) propagate with their actual message, not masked as
  "Install @huggingface/transformers...".
- src/providers/embedding/{local,clip}.ts: type module from
  typeof import("@huggingface/transformers") so PretrainedModelOptions
  flows through; drop hand-rolled aliases and @ts-ignore. Cast at
  assignment sites (pipeline return union isn't structurally assignable
  to our narrow FeatureExtractor / ClipPipeline shapes).

Tests added where coverage was zero (would have caught the dtype
regression):

- test/local-embedding-provider.test.ts (3 tests): unavailable-path
  install hint; pipeline called with dtype:q8 + extractor options +
  mapped Float32Array result; embedBatch shape.
- test/clip-embedding-provider.test.ts (5 tests): unavailable-path;
  text pipeline dtype:q8 + result; embedBatch; embedImage with data:
  URL decode; custom model ID propagation.
- test/reranker.test.ts: positive-path using vi.doMock + resetModules.

Other:
- src/huggingface.d.ts deleted (package ships its own types).
- src/xenova.d.ts removed.
- src/providers/embedding/clip.ts: inline single-use DIMENSIONS constant.
- tsdown.config.ts: trim neverBundle list and comment.
- README.md L1267: BGE-small -> Xenova/all-MiniLM-L6-v2 (was always wrong).
- 16 docs: install commands + prose mentions across main README, 11
  translations, SECURITY.md, 2 benchmark docs, benchmark script.
- Model IDs (Xenova/all-MiniLM-L6-v2, Xenova/clip-vit-base-patch32,
  Xenova/ms-marco-MiniLM-L-6-v2) kept — HF Hub repo names, still valid.

Closes #1095. Fixes #479.

Verified: 1424/1424 tests pass, build clean, tsc clean on migrated files.

* test(embedding): add v4 smoke test, harden import errors, expand CI matrix

Review follow-ups for #1096:
- env-guarded non-mocked smoke test (RUN_HF_SMOKE=1) loading real
  Xenova/all-MiniLM-L6-v2, asserts 384 finite dims; skipped by default
- selective ERR_MODULE_NOT_FOUND handling in local/clip providers so real
  init errors propagate (checks err.code and err.cause.code to handle
  vitest mock-factory wrapping)
- CLIP install hint made embedding-agnostic (loader serves text + image)
- afterEach mock cleanup in doMock-based provider/reranker tests
- CI Node matrix: [20, 22] -> [20, 22, 24, 26] across ubuntu/macos

* refactor(embedding): drop err.cause check, use manual mock for missing-module tests

The .cause branch in the ERR_MODULE_NOT_FOUND check existed only to
accommodate vitest's mock-factory wrapping, not a real Node loader
behavior. Replace it with a manual mock fixture (__mocks__/@huggingface/
transformers.ts) that throws a Node-shaped error at module top-level,
bypassing vitest's factory wrapper so the import rejects with err.code
set directly.

Production code now checks only err.code === 'ERR_MODULE_NOT_FOUND',
matching real Node behavior. Tests verify the same public contract
without coupling production code to the test framework.
2026-07-29 10:21:54 +01:00

5.7 KiB

Security Policy

Reporting a vulnerability

Do not open a public GitHub issue for a suspected vulnerability.

Use one of:

  • GitHub Security Advisories (preferred) — private report form at https://github.com/rohitg00/agentmemory/security/advisories/new. GitHub routes the report to the Maintainers, assigns a GHSA identifier, and keeps you in a private thread until the fix ships. All sensitive details (stack traces, credentials, exploit payloads) stay end-to-end within GitHub's security infrastructure — use this channel whenever possible.
  • Encrypted email (fallback) — if GitHub is unavailable or the issue cannot be described in the GHSA form, send an encrypted message to ghumare64@gmail.com with subject agentmemory security. Encrypt with the Maintainer public keys published at https://github.com/rohitg00.gpg (PGP) and https://github.com/rohitg00.keys (SSH for verification); attach your own public key so we can reply encrypted. Plaintext email is accepted only as a last resort — prefer GHSA.

Include, at minimum:

  • agentmemory version (npm view @agentmemory/agentmemory version against your install).
  • The affected surface — REST endpoint, MCP tool, hook, CLI flag, or filesystem layout.
  • A minimal reproduction — prefer one curl invocation or one MCP tool call plus the environment state required.
  • Impact, in your own words.

What we do with it

  1. Acknowledge within 72 hours (target: 24).
  2. Triage — confirm reproduction, assign a severity using CVSS 3.1, and give you a rough timeline.
  3. Fix in a private branch. Draft a GitHub Security Advisory with the patched version, CWE, CVSS vector, affected versions, and attribution to you (unless you prefer anonymity).
  4. Coordinate disclosure — we agree a disclosure date with you. Default window is 30 days from acknowledgment for straightforward vulnerabilities, up to 90 days for ones that need a deep refactor.
  5. Publish — release the patched version on npm, publish the advisory, update CHANGELOG.md under a ### Security section for the release, notify downstream scanners.

Supported versions

Version Security fixes?
Latest minor (currently 0.9.x) Yes
Previous minor (currently 0.8.x) Critical / High severity only, for 90 days after a new minor is released
Older No

At v1.0 this policy switches to a stated LTS window per the roadmap.

Scope

In scope:

  • The @agentmemory/agentmemory server (REST + MCP surface, hook handlers, state store).
  • The @agentmemory/mcp standalone MCP server.
  • The @agentmemory/fs-watcher connector.
  • First-party integrations under integrations/ (hermes/, openclaw/, filesystem-watcher/).
  • The Claude Code plugin under plugin/.

Out of scope:

  • Third-party MCP clients consuming agentmemory — report to those projects.
  • iii-sdk upstream — report to the iii project.
  • The marketing site under website/ unless the issue affects user security (XSS against visitors, credential leak in build output).

Supply-chain stance

agentmemory ships pre-built artifacts in the npm tarball — dist/ is bundled at publish time, not built from node_modules at install time. The package's runtime dependency tree is intentionally small (6 production deps: @anthropic-ai/sdk, @anthropic-ai/claude-agent-sdk, @clack/prompts, dotenv, iii-sdk, zod) plus an optional set guarded behind optionalDependencies for embeddings.

No lockfile is committed (#540). The reasoning:

  • The npm tarball ships pre-built dist/ — fresh installs don't compile from source, so no lockfile is consulted at the user's install step.
  • The lockfile only affects contributor-local builds. Pinning it would shift the supply-chain attack surface from "what npm resolves today" to "what was resolved when the lockfile was last regenerated," which is a different tradeoff, not strictly better.
  • We use SemVer ranges (^x.y.z) on the published deps so security patches reach users without a re-release.

If you ship agentmemory inside a hardened pipeline that requires reproducible installs, the recommended path is:

  1. npm install --legacy-peer-deps against the published tarball in a controlled environment.
  2. npm shrinkwrap to produce a versioned npm-shrinkwrap.json that travels with your deployment.
  3. Audit node_modules/ once at that point and republish internally.

CI runs npm install --package-lock-only --legacy-peer-deps --no-audit --no-fund then npm ci against that generated lockfile, so every test job builds against a fully resolved tree. The lockfile is regenerated on each CI run rather than checked in, which keeps the published tarball aligned with whatever SemVer-compatible patch level was current at release time.

Supply-chain monitoring we already do:

  • Dependabot opens PRs for every minor/patch bump on the production dep list (visible in the open PRs).
  • Every PR runs the full test suite on ubuntu-latest + macos-latest, Node 20 + 22, before any merge.
  • optionalDependencies (@huggingface/transformers, transitively onnxruntime-node, etc.) are guarded by try { await import("...") } catch so a missing or compromised optional dep cannot break the core runtime path.

If you find a malicious package in our dep tree, file via the GHSA flow at the top of this document — that's the fastest path to a fixed release on npm.

Past advisories

See the .github/security-advisories/ directory for advisory drafts. Published advisories (with assigned GHSA IDs) live at https://github.com/rohitg00/agentmemory/security/advisories.

Safe harbor

Good-faith research, reported privately, does not get legal heat from the project. Research targeting third-party deployments of agentmemory is not covered — that's between you and the deployer.