Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Releases: EvoMap/evolver

v1.86.0

23 May 08:30

Choose a tag to compare

v1.86.0 β€” Optional OS-keychain backing for workspace-id (issue #111 Phase 1)

Highlights

  • Workspace-id can now live in the OS keychain instead of <workspace>/.evolver/workspace-id. Closes the same-uid readability gap left by v1.83's FS-only secret. Optional @napi-rs/keyring addon backs the secret onto macOS Keychain Services / libsecret on Linux / Windows Credential Manager. Behaviour is identical to v1.85.x for any deployment that doesn't install the optional addon.

Added

  • EVOLVER_WORKSPACE_KEYCHAIN={auto,force,off} (default: auto) β€” controls how getWorkspaceId() resolves the per-workspace secret:
    • auto β€” try keychain, fall back to FS on any failure. Existing FS secrets migrate into the keychain on first call. The FS file is intentionally kept so bun-compiled binaries (which can't sideload the .node addon yet β€” Phase 2) keep agreeing with node-CLI sessions on the same id.
    • force β€” keychain only. Throws if the addon isn't loaded, the keyring reports unavailable mid-call, or write fails. Use in CI to assert the addon is present and never falls back to FS plaintext.
    • off β€” skip keychain entirely; FS only.
  • EVOLVER_WORKSPACE_ID env override is unchanged and still wins over both keychain and FS.

Fixed

  • Lifecycle hello no longer mints a fresh node_id when MailboxStore is empty but ~/.evomap/node_id still exists. Any install whose state.json was created or wiped after the legacy file existed (upgrade from a pre-lifecycle version, partial recovery flow) registered a brand-new A2ANode under the same owner on the next daemon boot, abandoning the original record's stake / reputation / aliases as an orphan in the web UI. The reader now consults the legacy hex file between the store lookup and the crypto.randomBytes(6) fallback so both paths agree on a single identity.

Refactored

  • Lifecycle's legacy node_id reader now routes through paths.getEvomapPath() so EVOLVER_HOME honours both reader and writer in lockstep (the writer in src/gep/a2aProtocol.js was consolidated onto the same helper in #114). No behaviour change beyond unifying the override semantics.

Internal

  • End-to-end test for the issue #540 advisory-signal fix and a new CHANGELOG release-section integrity guard (#113 / #115). PR #105 added function-level tests; the new e2e tests run collect.js outputs through gep/signals.js to lock in the full pipeline so a future refactor that breaks either end fails loudly. The new scripts/check-changelog.js + pre_publish_check.js integration catches the misattribution pattern that bit us in #540 / PR #107 (an entry filed under ## [X.Y.Z] after vX.Y.Z was already published). extractSection is line-anchored so fenced code samples don't trigger false drift; EVOLVER_CHANGELOG_GUARD_SOFT=1 is available as an interim escape hatch while private-dev still relies on public-mirror tags.

Migration

  • Existing v1.85.x installs that haven't installed @napi-rs/keyring continue to use the FS path with no behaviour change.
  • To opt in: npm install @napi-rs/keyring (already in optionalDependencies, so a fresh npm install will pull it on supported platforms automatically).
  • To enforce keychain-only on CI: export EVOLVER_WORKSPACE_KEYCHAIN=force.

Issue / PR

v1.85.3

23 May 04:30

Choose a tag to compare

v1.85.3 β€” Hotfix release

This release fixes two install-path regressions reported in #540 and #541 / #542. Both were silent breakages that landed in the v1.85.1 / v1.85.2 line and only surfaced under specific install layouts.

Fixed

  • Codex session-end hook no longer crashes with SyntaxError: Identifier 'path' has already been declared on fresh install (#542). Previous releases ran evolver setup-hooks --platform=codex and produced a parse-broken hook β€” Codex session-end was entirely blocked with no user-side workaround. The duplicate const path = require('path') is removed, and a new node --check regression suite now guards every file in src/adapters/scripts/ plus the CLI entry, so a parse-time failure in entry-point scripts (which the existing vitest suite never loads) cannot reach npm again.

  • getRepoRoot() no longer escapes node_modules to pick up an unrelated outer .git (#541). A global install (e.g. macOS Homebrew at /opt/homebrew/lib/node_modules/@evomap/evolver) used to walk past its own node_modules and resolve repoRoot to /opt/homebrew/.git, silently sending workspaceRoot / memoryDir / evolutionDir to a directory that doesn't belong to the user. The walk now stops at the parent of the nearest node_modules ancestor: local installs still find the user project's .git, global installs fall back to the install dir, dev clones keep the original unbounded walk. EVOLVER_REPO_ROOT remains the explicit override.

Upgrade path

npm install -g @evomap/evolver@latest

After upgrade, re-run evolver setup-hooks --platform=codex if you previously hit the SyntaxError to regenerate a clean hook script.

v1.85.2

22 May 14:51

Choose a tag to compare

Release v1.85.2

v1.85.1

22 May 11:05

Choose a tag to compare

Fixed

  • Stop hook no longer re-injects the evolution receipt as a user prompt.
    The hook used to emit followup_message, which by Claude Code spec
    feeds its value back into the next inference round. Agents kept
    "responding" to their own evolution log line β€” visible to users as an
    unexplained extra reasoning turn after every task. The hook now emits
    only systemMessage, a UI-only notification.

  • Cursor compatibility for the Stop hook. Cursor's Claude Code-compat
    runtime currently treats systemMessage as a user prompt as well.
    When Cursor is detected (via TERM_PROGRAM=cursor, CURSOR_TRACE_ID,
    CURSOR_SESSION_ID, or the manual override EVOLVER_HOOK_HOST=cursor),
    the hook omits systemMessage too. The receipt is always appended to
    ~/.evolver/logs/evolution.log (override path with
    EVOLVER_HOOK_LOG_DIR) so it is never silently lost. Set
    EVOLVER_HOOK_VERBOSE=1 to force the inline notification on under
    Cursor for debugging.

  • Stop hook process now exits promptly. A 7 s setTimeout watchdog
    was held open for the full duration on every session end because
    it was never cleared after stdin closed. Now cleared explicitly.

  • evolver --review no longer raises memory_missing / user_missing /
    session_logs_missing on every cycle when running on Codex (#540).

    Codex doesn't generate a workspace-root MEMORY.md / USER.md and
    doesn't expose readable session-transcript files. collect.js now
    falls back, in order: MEMORY.md / USER.md β†’ the
    <!-- evolver-evolution-memory --> section that setup-hooks injects
    into the workspace's AGENTS.md / CLAUDE.md β†’ the tail of
    memory_graph.jsonl (last 5 outcomes). README adds a "Codex caveats"
    subsection.

Security

  • Per-workspace random secret replaces plain-text cwd self-tag in
    memory_graph.jsonl (#109).
    v1.85.0 + PR #108 introduced a cwd
    field on every memory-graph entry to scope reads at the user-level
    fallback path. The Cursor Bugbot round-3 advisory pointed out that
    cwd is a self-report β€” any process under the same uid could write
    entries claiming a different workspace's cwd and poison its reads.
    This release adds paths.getWorkspaceId(), which lazily creates
    <workspace>/.evolver/workspace-id (mode 0600, 32-hex random) and
    stamps every new entry with workspace_id. The reader uses a
    three-tier policy: prefer workspace_id matching when the current
    workspace has a secret, fall back to legacy cwd-only matching when
    it doesn't (clean upgrade for pre-existing entries), drop entries
    that have neither. The secret file is created with
    O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW after an lstat pre-check,
    so a pre-placed symlink can't redirect the write outside the
    workspace. EVOLVER_WORKSPACE_ID env var remains as an explicit
    override / escape hatch. Note: in the co-uid threat model, any process
    under the same uid can still read the FS secret β€” migrating to an
    OS keychain is tracked as a follow-up.

PRs: #109, #110, #540


πŸ€– Generated with Claude Code

v1.85.0

22 May 04:08

Choose a tag to compare

Release v1.85.0

v1.84.2

22 May 03:15

Choose a tag to compare

v1.84.2

Fixes for Codex / Claude Code hook adapters reported on the public
issue tracker (#536–#540), plus security hardening surfaced during
review.

Hook adapter fixes

  • #536 Codex session-end no longer "records to nowhere" on
    npm-global installs. _runtimePaths.js is now copied alongside the
    hook scripts and resolves @evomap/evolver via system roots; falls
    back to ~/.evolver/memory/evolution/memory_graph.jsonl if the
    evolver root is read-only.
  • #537 Codex session-end no longer prints The system cannot find the path specified. on Windows. execSync shell strings
    with POSIX 2>/dev/null redirects are replaced with argv-array
    spawnSync, so cmd.exe never sees /dev/null.
  • #538 evolver setup-hooks --uninstall now removes everything
    it installs: hook scripts (including _runtimePaths.js), the
    evolution-memory section in AGENTS.md / CLAUDE.md, and
    codex_hooks = true plus the empty [features] block in
    .codex/config.toml. Uninstall also catches evolver-owned hook
    entries even when the _evolver_managed marker has been stripped.
  • #539 evolver setup-hooks no longer overwrites user-installed
    hooks under the same event. mergeWithHooksUnion keeps existing
    user Stop / SessionStart / PostToolUse entries and appends
    evolver entries alongside them.
  • #540 The review engine no longer hard-depends on repo-local
    MEMORY.md / USER.md. It first reads platform-supplied
    additionalContext / session_state, then falls back to
    evolver-managed memory_graph.jsonl, then to the markdown files.
    Codex's lack of session-log hooks is documented as a platform
    limitation (not an evolver bug).

Security hardening

  • Refuse to follow symlinks at adapter config dirs (.codex,
    .claude, .cursor, .kiro, .opencode) or at their nested
    hooks/ and plugins/ subdirs.
  • Refuse to copy a hook script when the destination path is a
    pre-planted symlink (would otherwise let a hostile workspace
    redirect overwrites to arbitrary writable files).
  • findEvolverRoot() no longer trusts process.cwd() when
    resolving @evomap/evolver β€” only system module roots are
    searched, so a workspace-controlled node_modules/@evomap/evolver
    cannot redirect the memory graph and inject prompt-injection
    content via additionalContext.
  • Codex cleanConfigToml only drops the [features] header when
    the section is genuinely empty; user entries under [features]
    are preserved across uninstall.
  • evolver-session-end runGit distinguishes "command failed"
    from "command succeeded with empty output", so an empty merge
    doesn't fall through to the working-tree diff and surface
    unrelated unstaged changes as a session outcome.

Reviewed by

Cursor Bugbot agentic security review (7 rounds), final commit
f32f709b75eeb0187780318a9a8a602a53214c4d. 99 / 99 adapter tests
passing.

v1.84.1

18 May 20:09

Choose a tag to compare

Release v1.84.1

v1.84.0

17 May 03:25

Choose a tag to compare

Release v1.84.0

v1.83.0

16 May 13:30

Choose a tag to compare

[1.83.0] - 2026-05-16

Added β€” Publish β†’ recall round-trip verification (#53, #56)

After every successful Hub publish (Capsule, AntiPattern, or SkillBundle),
the daemon now confirms that the asset can actually be recalled from
Hub. A new src/gep/recallVerifier.js worker runs Phase 2 deterministic
asset_id lookups on a sampling of published assets, with exponential
backoff to absorb indexing latency. Outcomes (roundtrip_ok /
roundtrip_missing / roundtrip_mismatch / verification_skipped) land
in memory_graph.jsonl as kind=recall_verify events.

A new scripts/recall-verify-report.js aggregates those events into a
Markdown table grouped by asset type with success rate + p50/p95/p99
latency. Exit code is a ship-gate: 0 when every asset type meets β‰₯ 0.95
success and zero mismatches, exit 2 otherwise. Designed to plug into
deploy.sh as a pre-publish guard.

This closes the "daemon knows results" theme set up by v1.82.0's open-PR
overlap detection: v1.82.0 made the daemon aware of in-flight work so it
stops re-inventing it; v1.83.0 makes the daemon aware of whether its own
uploaded experience is actually retrievable later. Together they bound
the daemon's two main blind spots β€” "what other people are doing" and
"whether what I just uploaded actually stuck."

Architecture

Layer Behavior
src/gep/recallVerifier.js (new) Bounded ring queue (default 256), single-flight by asset_id, sample-rate gate, async setInterval worker (unref'd so process exit is unblocked). Retries on missing with [5s, 15s, 60s] backoff up to 3 attempts.
src/gep/hubSearch.js Phase 2 fetch logic extracted into a reusable fetchAssetById(assetId, opts) helper. Cache-first ordering preserved so hot payloads return instantly even when the search-phase budget is exhausted.
Three publish hooks solidify.js capsule + anti-pattern, skill2gep.js publishBundleChannel. Each enqueues only after res.ok && !res.dry_run, and uses the sanitized capsule's asset_id so the verifier looks up the same hash the Hub indexed.
scripts/recall-verify-report.js (new) Markdown report with monotonic gate severity (RED never downgrades to YELLOW).
index.js [RecallVerify] ENABLED/DISABLED startup banner, starts the worker once, with sample-rate range clamping.

Mismatch detection

verifyOnce recomputes computeAssetId on the recalled body and
compares to results[0].asset_id. A mismatch means the Hub re-encoded or
corrupted the asset between publish and fetch β€” surfaced as
roundtrip_mismatch and flips the gate to RED.

Configuration (all default-on)

Env var Default Purpose
EVOLVE_RECALL_VERIFY 1 Master switch
EVOLVE_RECALL_VERIFY_SAMPLE_RATE 1.0 Fraction of publishes to verify (clamped to [0, 1])
EVOLVE_RECALL_VERIFY_QUEUE_MAX 256 Ring queue size
EVOLVE_RECALL_VERIFY_INITIAL_WAIT_MS 5000 First-attempt delay
EVOLVE_RECALL_VERIFY_POLL_MS 5000 Worker tick rate
EVOLVE_RECALL_VERIFY_ATTEMPTS 3 Retries on roundtrip_missing
EVOLVE_RECALL_VERIFY_FETCH_TIMEOUT_MS 8000 Phase 2 timeout

Hub mirror staging

recall_verify is intentionally not in HUB_SYNC_KIND_ALLOWLIST on
first ship β€” local-only until Hub side confirms it accepts the schema.
One-line follow-up patch enables mirroring later.

Hardened β€” five Bugbot review iterations on the publish/verify

data flow

PR #53 + #56 went through five Cursor Bugbot rounds. Each surfaced a
real defect that would have shipped silently:

  • schemas/protocol obfuscation gap. recallVerifier.js was missing
    from public.manifest.json:obfuscate; would have shipped in plaintext
    to npm.
  • Phase 2 cache bypass regression. Refactored time-budget check
    ordered before cache lookup, so hot payloads were silently re-fetched
    (or worse, dropped) under deadline pressure.
  • Gate severity could downgrade. recall-verify-report.js's gate
    loop overwrote later rows, so AntiPattern@RED followed by Capsule@YELLOW
    reported YELLOW β€” misleading dashboards even though exit code was
    correct. Replaced with monotonic escalation (RANK ordinal).
  • Wrong asset_id used for recall lookup. solidify.js's capsule and
    anti-pattern paths enqueued the pre-sanitize asset_id for
    verification, but Hub indexes by the sanitized hash. Any PII
    redaction would have produced persistent false roundtrip_missing
    results, dragging the ship gate toward RED forever. Both paths now
    recompute on the sanitized capsule and pass that hash to the verifier.
  • Sample-rate banner-vs-implementation drift. index.js clamped
    sample rate to [0, 1] but _getSampleRate() did not, so a negative
    env value silently disabled all verification while the banner still
    reported 1.0.
  • Dry-run guard masked Hub rejections. else if (res && res.dry_run)
    classified {ok:false, dry_run:true} (a real Hub reject during
    dry-run) as a clean dry-run skip. Now requires res.ok && res.dry_run.

Notes for operators

  • This release is enabled-by-default; no opt-in required to start
    collecting recall_verify events. To disable, set
    EVOLVE_RECALL_VERIFY=0 before the daemon starts.
  • A 30-minute real-Hub daemon smoke + recall-verify-report ship-gate
    read is the recommended pre-deploy ritual once a non-trivial volume of
    publishes has accumulated.
  • Memory graph events with kind=recall_verify are local-only this
    release; the next release will add them to the Hub mirror allowlist
    once Hub-side schema acceptance is confirmed.

v1.82.1

16 May 11:38

Choose a tag to compare

[1.82.1] - 2026-05-16

Fixed β€” P0: force-update no longer overwrites user projects (#51, #52)

executeForceUpdate previously used getRepoRoot() to decide which
directory to update. getRepoRoot() preferentially returns the user's
surrounding git project β€” correct for evolution signals, catastrophic
for "delete everything except .git/node_modules/memory/MEMORY.md and copy
the evolver tarball on top of it." When the Hub pushed a force_update
directive (via heartbeat or event poll) to a node whose process.cwd() was
inside any other git repository, the deletion loop ran inside that repo
and the user lost their files.

This was reproducible end-to-end via cd /tmp/anything-with-.git; trigger executeForceUpdate β€” the user's files were replaced with the
evolver package contents and the user's package.json was rewritten to
@evomap/evolver. We confirmed the bug actually triggered against
evolver-private-dev itself during fix development (recovered via
git reset --hard HEAD) β€” that's the strongest possible evidence for
the severity.

The fix introduces a new paths.getEvolverInstallRoot() that returns
the evolver package directory (path.resolve(__dirname, '..', '..'))
regardless of process.cwd() or EVOLVER_REPO_ROOT. forceUpdate.js
now uses this INSTALL_ROOT for every read, delete, and copy. A
defense-in-depth guard refuses the update entirely if
INSTALL_ROOT/package.json does not name @evomap/evolver β€” so any
future path-resolution regression fails closed instead of wiping an
unrelated directory. The staging tmp directory also moves to
os.tmpdir() because INSTALL_ROOT's parent is typically not writable
under a global npm install.

Recommended action: upgrade immediately. Anyone running
evolver --loop from inside any git repository is at risk under
v1.82.0 and earlier.

Credit: issue reported by cloudcarver, codex POC reproducer
diagnosed by cloudcarver, fix landed in #52.