@evomap/evolver 1.89.3 → 1.89.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/BUGBOT.md +182 -0
- package/.env.example +68 -0
- package/.git-commit-guard-token +1 -0
- package/.github/CODEOWNERS +63 -0
- package/.github/ISSUE_TEMPLATE/good_first_issue.md +23 -0
- package/.github/pull_request_template.md +45 -0
- package/.github/workflows/test.yml +75 -0
- package/CHANGELOG.md +1237 -0
- package/README.md +86 -528
- package/README.public.md +569 -0
- package/SECURITY.md +108 -0
- package/assets/gep/events.jsonl +3 -0
- package/examples/atp-consumer-quickstart.md +100 -0
- package/examples/hello-world.md +38 -0
- package/index.js +30 -1
- package/package.json +6 -17
- package/proxy-package.json +39 -0
- package/public.manifest.json +143 -0
- package/src/config.js +23 -0
- package/src/evolve/guards.js +721 -1
- package/src/evolve/pipeline/collect.js +1283 -1
- package/src/evolve/pipeline/dispatch.js +421 -1
- package/src/evolve/pipeline/enrich.js +440 -1
- package/src/evolve/pipeline/hub.js +319 -1
- package/src/evolve/pipeline/select.js +274 -1
- package/src/evolve/pipeline/signals.js +206 -1
- package/src/evolve/utils.js +264 -1
- package/src/evolve.js +350 -1
- package/src/experiment/agentRunner.js +229 -0
- package/src/experiment/cli.js +159 -0
- package/src/experiment/comparison.js +233 -0
- package/src/experiment/metrics.js +75 -0
- package/src/forceUpdate.js +147 -59
- package/src/gep/a2aProtocol.js +4455 -1
- package/src/gep/antiAbuseTelemetry.js +233 -0
- package/src/gep/autoDistillConv.js +205 -1
- package/src/gep/autoDistillLlm.js +315 -1
- package/src/gep/candidateEval.js +92 -1
- package/src/gep/candidates.js +198 -1
- package/src/gep/contentHash.js +30 -1
- package/src/gep/conversationSniffer.js +266 -1
- package/src/gep/crypto.js +89 -1
- package/src/gep/curriculum.js +163 -1
- package/src/gep/deviceId.js +218 -1
- package/src/gep/envFingerprint.js +118 -1
- package/src/gep/epigenetics.js +31 -1
- package/src/gep/execBridge.js +711 -1
- package/src/gep/explore.js +289 -1
- package/src/gep/hash.js +15 -1
- package/src/gep/hubFetch.js +359 -1
- package/src/gep/hubReview.js +207 -1
- package/src/gep/hubSearch.js +526 -1
- package/src/gep/hubVerify.js +306 -1
- package/src/gep/learningSignals.js +89 -1
- package/src/gep/memoryGraph.js +1374 -1
- package/src/gep/memoryGraphAdapter.js +203 -1
- package/src/gep/mutation.js +203 -1
- package/src/gep/narrativeMemory.js +108 -1
- package/src/gep/openPRRegistry.js +205 -1
- package/src/gep/personality.js +423 -1
- package/src/gep/policyCheck.js +599 -1
- package/src/gep/prompt.js +836 -1
- package/src/gep/recallInject.js +409 -1
- package/src/gep/recallVerifier.js +318 -1
- package/src/gep/reflection.js +177 -1
- package/src/gep/sanitize.js +9 -0
- package/src/gep/selector.js +602 -1
- package/src/gep/skillDistiller.js +1294 -1
- package/src/gep/solidify.js +1699 -1
- package/src/gep/strategy.js +136 -1
- package/src/gep/tokenSavings.js +88 -1
- package/src/gep/validator/sandboxExecutor.js +29 -1
- package/src/gep/workspaceKeychain.js +174 -1
- package/src/proxy/extensions/traceControl.js +99 -1
- package/src/proxy/index.js +10 -1
- package/src/proxy/inject.js +52 -1
- package/src/proxy/lifecycle/manager.js +19 -0
- package/src/proxy/mailbox/store.js +2 -1
- package/src/proxy/router/messages_route.js +5 -2
- package/src/proxy/trace/extractor.js +646 -1
- package/src/proxy/trace/usage.js +105 -1
- package/CONTRIBUTING.md +0 -19
- package/assets/cover.png +0 -0
- package/scripts/a2a_export.js +0 -63
- package/scripts/a2a_ingest.js +0 -79
- package/scripts/a2a_promote.js +0 -118
- package/scripts/analyze_by_skill.js +0 -121
- package/scripts/build_binaries.js +0 -479
- package/scripts/check-changelog.js +0 -166
- package/scripts/extract_log.js +0 -85
- package/scripts/generate_history.js +0 -75
- package/scripts/gep_append_event.js +0 -96
- package/scripts/gep_personality_report.js +0 -234
- package/scripts/human_report.js +0 -147
- package/scripts/recall-verify-report.js +0 -234
- package/scripts/recover_loop.js +0 -61
- package/scripts/refresh_stars_badge.js +0 -168
- package/scripts/seed-merchants.js +0 -91
- package/scripts/suggest_version.js +0 -89
- package/scripts/validate-modules.js +0 -38
- package/scripts/validate-suite.js +0 -78
- package/skills/index.json +0 -14
- /package/assets/gep/{genes.seed.json → genes.json} +0 -0
- /package/{skills → bundled-skills}/_meta/SKILL.md +0 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,1237 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@evomap/evolver` are tracked here.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [1.89.4] - 2026-06-11
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Proxy traces now queue for outbound Hub sync (#232).** Encrypted proxy trace
|
|
12
|
+
rows are written to the mailbox as `proxy_trace` outbound messages and wake the
|
|
13
|
+
outbound sync loop, so trace capture no longer stops at the local JSONL file.
|
|
14
|
+
Upload preflight now caps pending trace messages by type, reports skipped
|
|
15
|
+
uploads with bounded warnings, and keeps plaintext trace upload disabled unless
|
|
16
|
+
`EVOMAP_PROXY_TRACE_UPLOAD_PLAINTEXT=danger` is explicitly set.
|
|
17
|
+
- **Fresh proxy installs default to the canonical Hub URL (#230).**
|
|
18
|
+
`EvoMapProxy` now uses `resolveHubUrl()` instead of an empty fallback, so
|
|
19
|
+
`evolver login` leaves the proxy Hub-connected out of the box while still
|
|
20
|
+
respecting explicit `opts.hubUrl` and local insecure-Hub overrides.
|
|
21
|
+
- **Release-commit test runs no longer trip their own release-window guard
|
|
22
|
+
(#229).** The evolve guard test disables `EVOLVE_RELEASE_WINDOW_MS` in its
|
|
23
|
+
"normal conditions" fixture, keeping required CI green on release bump commits.
|
|
24
|
+
- **Reverse leak scanning skips path/URL-shaped environment values (#233).**
|
|
25
|
+
CI workspace paths such as `GITHUB_WORKSPACE`, `RUNNER_WORKSPACE`, and npm
|
|
26
|
+
local-prefix values no longer trigger false secret-leak failures, while
|
|
27
|
+
non-path secret values remain reverse-detected.
|
|
28
|
+
|
|
29
|
+
## [1.89.3] - 2026-06-10
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- **Daemon lock path + lease staleness extracted into a shared helper (#176).**
|
|
34
|
+
The session-start hook's auto-restart guard used to replicate the daemon's
|
|
35
|
+
lock resolution (`EVOLVER_LOCK_DIR` override, `~/.evomap/instance.lock`
|
|
36
|
+
default, lease TTLs, mtime staleness) inline, which could silently diverge
|
|
37
|
+
whenever `index.js` changed. Both now import
|
|
38
|
+
`src/adapters/scripts/_lockPaths.js` (fs/os/path-only, so the daemon module
|
|
39
|
+
still stays out of the hook's require graph). The helper ships in the hook
|
|
40
|
+
copy list and the hook requires it at load time, so a broken deployment
|
|
41
|
+
fails loud instead of silently disabling daemon auto-restart. No behavior
|
|
42
|
+
change for correctly-deployed installs.
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
|
|
46
|
+
- **Anti-abuse heartbeat telemetry (default on, explicit opt-out).** Hub
|
|
47
|
+
heartbeats (both the canonical client heartbeat and the proxy lifecycle
|
|
48
|
+
heartbeat) now attach a privacy-preserving `meta.anti_abuse` envelope:
|
|
49
|
+
schema/source-confidence labels, HMAC-based device/workspace pseudonyms
|
|
50
|
+
(only when an anti-abuse salt is configured — raw ids never leave the
|
|
51
|
+
node), integrity hashes of `package.json` / the CLI entry / lockfiles, a
|
|
52
|
+
local security-boundary summary, and explicit placeholders for fields the
|
|
53
|
+
Hub must observe server-side instead of trusting the client. Opt out with
|
|
54
|
+
`EVOLVER_ANTI_ABUSE_TELEMETRY=off` (an empty/blank value counts as unset
|
|
55
|
+
and keeps the default on). The Hub currently accepts the envelope under
|
|
56
|
+
heartbeat `meta` without persisting it.
|
|
57
|
+
- **Structured force-update failure reporting.** `executeForceUpdate` now
|
|
58
|
+
returns a structured `{ ok, code, detail }` on every failure branch instead
|
|
59
|
+
of a bare `false`, and `reportForceUpdateOutcome` encodes it into the existing
|
|
60
|
+
`last_update.error` field as `"code: detail"`. `EvolverUpgradeAttempt` rows now
|
|
61
|
+
distinguish `degit_failed`, `npx_not_found`, `degit_timeout`,
|
|
62
|
+
`download_incomplete`, `downloaded_version_mismatch`, `delete_failed`,
|
|
63
|
+
`copy_failed`, `current_version_unparsable`, `bad_required_version`,
|
|
64
|
+
`install_guard_name_mismatch`, `install_guard_unreadable`, and
|
|
65
|
+
`all_channels_exhausted` instead of all reading "executeForceUpdate returned
|
|
66
|
+
false" — queryable via `GROUP BY split_part(error, ':', 1)` with no hub schema
|
|
67
|
+
or DB migration. Backward compatible: success stays `true`, no-op/busy stay
|
|
68
|
+
sentinels.
|
|
69
|
+
|
|
70
|
+
### Fixed
|
|
71
|
+
|
|
72
|
+
- **Force-update no longer wedges a node on a mid-copy failure.** The Channel-1
|
|
73
|
+
install path deleted the old install (including `package.json`) in place
|
|
74
|
+
before copying the new tree, so a `cpSync` failure part-way through (ENOSPC, a
|
|
75
|
+
Windows lock outlasting the retries, a kill) left `INSTALL_ROOT` with no
|
|
76
|
+
`package.json`. The install-guard then refused every subsequent attempt with
|
|
77
|
+
`install_guard_unreadable` and never re-copied it — a permanent, self-
|
|
78
|
+
perpetuating wedge. `package.json` is now the install's atomic commit marker:
|
|
79
|
+
kept in place through the whole delete+copy and swapped in last via
|
|
80
|
+
tmp+rename, so a partial failure leaves the old `package.json` intact and the
|
|
81
|
+
node self-heals on the next tick.
|
|
82
|
+
- **Force-update commit marker recovery covers Windows and delete failures.**
|
|
83
|
+
Windows commits now keep a recoverable old-`package.json` backup while swapping
|
|
84
|
+
the new manifest, so a rename failure or interrupted commit can restore the
|
|
85
|
+
old marker instead of wedging the install guard. Delete failures for old
|
|
86
|
+
install entries now abort the update before the new manifest is committed,
|
|
87
|
+
preventing stale old files from being hidden behind a successful version bump.
|
|
88
|
+
|
|
89
|
+
## [1.89.2] - 2026-06-08
|
|
90
|
+
|
|
91
|
+
### Added
|
|
92
|
+
|
|
93
|
+
- **OAuth token auto-refresh in `evolver run`.** When a device-flow OAuth token
|
|
94
|
+
is present, a background timer refreshes it ~2min before expiry (and reschedules
|
|
95
|
+
after each refresh), so long-running agent loops never hit an expired `a2a`
|
|
96
|
+
token mid-request. The timer is `unref`'d so it never blocks process exit, and
|
|
97
|
+
it is a no-op for `node_secret`-only nodes (which have no refresh token).
|
|
98
|
+
|
|
99
|
+
## [1.89.1] - 2026-06-08
|
|
100
|
+
|
|
101
|
+
### Changed
|
|
102
|
+
|
|
103
|
+
- **License declared as GPL-3.0-or-later (corrected from a stale MIT notice).** The README
|
|
104
|
+
(and localized `zh-CN` / `ja-JP` / `ko-KR` variants) and `SKILL.md` now state
|
|
105
|
+
GPL-3.0-or-later, matching the project's copyleft + obfuscated-core distribution. No code or
|
|
106
|
+
runtime behavior changes.
|
|
107
|
+
- **Public README cleanup.** Documented the `EVOLVER_ISSUE_REPO` default as `EvoMap/evolver`
|
|
108
|
+
(matches `src/config.js`) and fixed the matching `index.js` issue hint and skillPublisher
|
|
109
|
+
attribution footer; normalized `evolver run` → `evolver` in examples; rewrote the Roadmap
|
|
110
|
+
into directional items pointing at GitHub Issues; removed a maintainer-only "Public Release"
|
|
111
|
+
section that referenced non-existent npm scripts and internal publish env vars; excluded
|
|
112
|
+
`.github/CODEOWNERS` from the public mirror so internal owner handles stay out.
|
|
113
|
+
|
|
114
|
+
### Fixed
|
|
115
|
+
|
|
116
|
+
- **Force-update no longer downgrades newer installs.** `required_version` is
|
|
117
|
+
treated as a minimum version floor for the no-op check, so a node already on a
|
|
118
|
+
newer `@evomap/evolver` release skips stale force-update directives instead
|
|
119
|
+
of reinstalling an older exact tag.
|
|
120
|
+
|
|
121
|
+
## [1.88.3] - 2026-06-06
|
|
122
|
+
|
|
123
|
+
### Fixed
|
|
124
|
+
|
|
125
|
+
- **Gene selection no longer self-reinforces a do-nothing gene (#562).** A `stable_no_error`
|
|
126
|
+
outcome (no error before/after, no parseable EvolutionEvent — i.e. the cycle did nothing
|
|
127
|
+
measurable) was tallied as a Bayesian "success" (score 0.6). Combined with the selector's
|
|
128
|
+
drift only diversifying when >1 gene matches a signal, a sole-matching auto-gene was
|
|
129
|
+
re-selected every `--loop` cycle, climbed edge confidence p → ~1.0, and was never banned
|
|
130
|
+
(the failure-streak ban never trips on "successes") — dominating selection ~99.7% of the
|
|
131
|
+
time while producing zero artifacts (reproduced against the published 1.88.2 binary).
|
|
132
|
+
`aggregateEdges` now tallies these zero-work outcomes as `inert`, apart from real
|
|
133
|
+
successes, so they build no confidence and do not count toward attempts; `getMemoryAdvice`
|
|
134
|
+
bans a gene after `GENE_INERT_BAN_STREAK` (default 8, env `EVOLVER_GENE_INERT_BAN_STREAK`)
|
|
135
|
+
consecutive inert outcomes with no real success on a signal, so the selector falls through
|
|
136
|
+
to mutation (null → fresh gene) and diversity is restored. Consecutive-trailing (reset by
|
|
137
|
+
any real success/failure), so a gene that ever does real work is never punished for idle
|
|
138
|
+
cycles.
|
|
139
|
+
- **Self-evolution validation commands no longer fail a cycle in user projects (#562).** Seed
|
|
140
|
+
genes (e.g. `gene_gep_repair_from_errors`) ship validation commands that target evolver's
|
|
141
|
+
own tree (`node scripts/validate-modules.js ./src/gep/...`). When evolver runs in a user's
|
|
142
|
+
project, `repoRoot` is that project and the script is absent, so the command could only fail
|
|
143
|
+
with "Cannot find module" — wrongly tanking the gene's `validation_pass_rate` on an
|
|
144
|
+
environment mismatch. `runValidationsOnce` now skips a `node <script>` validation command
|
|
145
|
+
whose (relative) script does not exist in `repoRoot`, excluding it from the pass/fail tally
|
|
146
|
+
instead of failing the cycle; a clear `[Solidify] Skipping validation command (script not in
|
|
147
|
+
repoRoot)` line replaces the confusing module-not-found error. Commands whose script is
|
|
148
|
+
present run unchanged.
|
|
149
|
+
|
|
150
|
+
## [1.88.2] - 2026-06-05
|
|
151
|
+
|
|
152
|
+
### Added
|
|
153
|
+
|
|
154
|
+
- **Runtime asset injection — general agents auto-use GEP assets (P4-c, #183).** Recall now injects relevant genes/capsules into the runtime context so a general agent picks them up without an explicit `gep_recall` call.
|
|
155
|
+
- **Cross-harness auto-exec recipes for Codex + OpenCode (P4-b, #184).** The Brain→Hand exec bridge gains Codex and OpenCode recipes, bringing cross-harness parity to the Claude Code bridge shipped in 1.88.1.
|
|
156
|
+
- **Reuse attribution reporting (P4-a Slice A, #186, default off).** Evolver can report which prior assets a successful cycle reused. Opt-in; no behavior change at default.
|
|
157
|
+
- **Heartbeat reports `force_update` outcome via the `last_update` field (#188).** The heartbeat loop now surfaces the result of a force-update so the Hub can observe update propagation.
|
|
158
|
+
- **Evolver recipe CLI — build/reuse (P4-c, #173).** New `recipe` subcommand for building and reusing recipes.
|
|
159
|
+
|
|
160
|
+
### Changed
|
|
161
|
+
|
|
162
|
+
- **ATP now consumes `@evomap/atp-sdk` for protocol enum constants (#185).** Protocol enums come from the shared SDK singleton instead of local literals, removing a drift source.
|
|
163
|
+
|
|
164
|
+
### Fixed
|
|
165
|
+
|
|
166
|
+
- **Release gate regressions (#192, #193).** `pre_publish_check.js` now runs the project's real test runner (`npm test` → `node --test`) instead of the no-longer-installed `npx vitest run`, and fails closed on a non-zero exit (the old `0-failures-but-nonzero-exit ⇒ pass` escape hatch is gone). The `npm test` script also no longer hard-codes the stable `--test-isolation=process` flag (Node >=22.23 only); process isolation is Node's default, so `npm test` runs on Node 22.12+ again. Plus: `normalizeWorkspaceIdentity` canonicalizes transcript paths via `realpath` (macOS `/var` vs `/private/var`) before provenance comparison, and session-start Kiro dedup vs. non-git-notice throttling now use separate state files.
|
|
167
|
+
- **ATP order/task association persisted + delivered bundle made GEP-valid (#187).**
|
|
168
|
+
- **Proxy forwards asset search as `GET /a2a/assets/search` (#189).**
|
|
169
|
+
|
|
170
|
+
## [1.88.1] - 2026-06-03
|
|
171
|
+
|
|
172
|
+
### Added
|
|
173
|
+
|
|
174
|
+
- **Conversation capability -> distilled gene (P2, `EVOLVER_CONV_DISTILL_ENABLED`).** The conversation sniffer's capability candidates (slug + transcript snippet) are now enqueued and, when enabled, run through a conversation-brief distillation (reusing the P3 claude-distill recipe + quality gate) to synthesize a candidate gene — closing discover->distill. **Shadow-only v1**: it logs a candidate gene for human review and NEVER upserts (conversation-snippet material is too thin to auto-upsert; run-green proves the validation command exits 0, not that the strategy is correct). New module `src/gep/autoDistillConv.js`; P2-owned `conv_distill.{by_hash,by_slug}` state (never the shared P3 scalars). New env: `EVOLVER_CONV_DISTILL_ENABLED` (off default), `CONV_SLUG_COOLDOWN_MS`, `EVOLVER_CONV_DISTILL_DUP_JACCARD`.
|
|
175
|
+
|
|
176
|
+
### Added
|
|
177
|
+
|
|
178
|
+
- **Autonomous LLM gene distillation (P3, `EVOLVER_AUTO_DISTILL_LLM`).** evolver can now
|
|
179
|
+
run the distillation prompt through a light read-only headless `claude` (reusing the P1
|
|
180
|
+
exec bridge) and turn a recurring success pattern into an LLM-quality Gene with no human in
|
|
181
|
+
the loop. New module `src/gep/autoDistillLlm.js`. Shadow-first: `off` (default) | `shadow`
|
|
182
|
+
(log candidate, no upsert) | `enforce` (upsert after a REAL quality gate). The gate:
|
|
183
|
+
structural validation + canonicalization, a Jaccard near-duplicate check, validation
|
|
184
|
+
normalization (strips policy-blocked + heavy test-suite commands, injects `node --version`),
|
|
185
|
+
and a run-green check (the gene's own validation must exit 0) BEFORE upsert. Never
|
|
186
|
+
auto-publishes (opt-in `EVOLVER_AUTO_DISTILL_LLM_PUBLISH=true`). New env:
|
|
187
|
+
`EVOLVE_DISTILL_MODEL`, `EVOLVE_DISTILL_MAX_TURNS`, `EVOLVE_DISTILL_TIMEOUT_MS`,
|
|
188
|
+
`EVOLVE_DISTILL_VALIDATION_TIMEOUT_MS`, `EVOLVER_AUTO_DISTILL_LLM_DUP_JACCARD`. Wired into
|
|
189
|
+
the daemon idle (OMLS) distill window next to the failure distiller; no behavior change at `off`.
|
|
190
|
+
|
|
191
|
+
### Added
|
|
192
|
+
|
|
193
|
+
- **Claude Code auto-exec bridge (`exec` subcommand).** `node index.js exec
|
|
194
|
+
--harness=claude-code [--once] [--max-cycles N]` closes the Brain→Hand loop on
|
|
195
|
+
Claude Code: it runs the Brain (`node index.js run`, bridge mode), scrapes the
|
|
196
|
+
emitted `sessions_spawn(...)`, and spawns a headless `claude -p` Hand to apply
|
|
197
|
+
the patch and run `solidify`, gated by a status file. Opt-in and shadow-first:
|
|
198
|
+
refuses unless `EVOLVE_EXEC_BRIDGE=true`. New module `src/gep/execBridge.js`
|
|
199
|
+
with a per-Hand hard timeout + process-group kill (a hung/permission-broken
|
|
200
|
+
Hand is killed and counts as a retry, never loops). The `exec` command exits
|
|
201
|
+
non-zero on any non-success outcome. New env: `EVOLVE_EXEC_BRIDGE`,
|
|
202
|
+
`EVOLVE_HAND_TIMEOUT_MS`, `EVOLVE_BRAIN_TIMEOUT_MS`, `EVOLVE_HAND_KILL_GRACE_MS`,
|
|
203
|
+
`EVOLVE_IDLE_SLEEP_MS`, `EVOLVE_HAND_MAX_BUF_BYTES`, `EVOLVE_HAND_MODEL`,
|
|
204
|
+
`EVOLVE_HAND_MAX_TURNS`, `EVOLVE_HAND_DANGEROUS`, `EVOLVE_EXEC_FALLBACKS`,
|
|
205
|
+
`CLAUDE_BIN`.
|
|
206
|
+
|
|
207
|
+
## [1.88.0] - 2026-06-03
|
|
208
|
+
|
|
209
|
+
### Added
|
|
210
|
+
|
|
211
|
+
- **Three-platform daemon autostart templates (#163).** The repo now ships
|
|
212
|
+
install helpers so the evolver daemon auto-starts on login with
|
|
213
|
+
restart-on-failure on every supported OS, matching the existing Linux
|
|
214
|
+
`scripts/evolver.service`: a macOS launchd agent
|
|
215
|
+
(`scripts/com.evomap.evolver.plist`, `KeepAlive.SuccessfulExit=false` +
|
|
216
|
+
`ThrottleInterval=5` mirroring `Restart=on-failure`/`RestartSec=5`) and a
|
|
217
|
+
Windows Task Scheduler installer (`scripts/install-evolver-windows.ps1`,
|
|
218
|
+
`-Install`/`-Uninstall`). The Windows shape launches via a generated
|
|
219
|
+
`%LOCALAPPDATA%\EvoMap\evolver-task-launcher.vbs` with the node/index paths
|
|
220
|
+
baked into VBScript literals and a single quoted task argument — `wscript`
|
|
221
|
+
is a Windows-subsystem host so no console window appears on logon, and
|
|
222
|
+
`WScript.Quit rc` propagates the exit code so restart-on-failure still
|
|
223
|
+
observes failures. All three platforms verified live through a real reboot.
|
|
224
|
+
|
|
225
|
+
- **Explicit signal injection channel (#172).** A new fifth injection point in
|
|
226
|
+
the signal-extraction stage reads user-declared signals from
|
|
227
|
+
`<gep-assets-dir>/pending_signals.json` (default `.evolver/gep`) and merges
|
|
228
|
+
them into a cycle's signal set, **bypassing the closed 20-entry
|
|
229
|
+
`OPPORTUNITY_SIGNALS` vocabulary**. This lets a human-verified, deterministic
|
|
230
|
+
capability whose intent no extractor can name still surface as a first-class
|
|
231
|
+
signal (previously it collapsed to a generic `capability_gap` and the
|
|
232
|
+
dedicated gene could never win selection). File-locked read-once semantics
|
|
233
|
+
(`consumePendingSignals()` empties the file after consuming), entries trimmed
|
|
234
|
+
and length-capped (≤200), non-fatal on error.
|
|
235
|
+
|
|
236
|
+
### Fixed
|
|
237
|
+
|
|
238
|
+
- **Heartbeat / daemon-survival resilience overhaul (#163).** A nine-round
|
|
239
|
+
hardening pass on the keep-alive path, addressing failure modes that left the
|
|
240
|
+
daemon silently dead after sleep/wake, idle windows, or concurrent startup:
|
|
241
|
+
- **Singleton-lock takeover is now atomic** — `linkSync` + `EEXIST` guard
|
|
242
|
+
(with a `renameSync` fallback for link-less filesystems), replacing a
|
|
243
|
+
non-atomic `unlinkSync`-before-write that could let two daemons both
|
|
244
|
+
"own" the lock and rotate `node_secret` against each other into mutual
|
|
245
|
+
401 backoff. Lock carries a `lease: true` marker + mtime refreshed every
|
|
246
|
+
`LOCK_REFRESH_MS` (60s Win / 120s POSIX); takeover honors both dead-PID
|
|
247
|
+
(`kill -0` ESRCH) and lease-expiry (mtime older than `STALE_LOCK_TTL_MS`,
|
|
248
|
+
3min Win / 5min POSIX).
|
|
249
|
+
- **sd_notify watchdog actually fires under systemd** — shells out to
|
|
250
|
+
`systemd-notify(1)` (with `NotifyAccess=all`) instead of the broken
|
|
251
|
+
`dgram` `unix_dgram` socket, which threw synchronously on node ≥22 and
|
|
252
|
+
swallowed `READY=1`/`WATCHDOG=1`.
|
|
253
|
+
- **Sleep/wake recovery** — a wall-clock drift detector (samples `Date.now()`
|
|
254
|
+
every 30s, treats a >90s jump as a wake; `cpuUsage`-vs-wall-clock delta
|
|
255
|
+
distinguishes CPU throttling from true sleep), a stuck-tick watchdog, and a
|
|
256
|
+
wake ritual that drains dead undici connections + restarts SSE.
|
|
257
|
+
- **Event-loop anchor + process survival** — a ref'd 10-min keepalive tick
|
|
258
|
+
keeps the loop alive and leaves a disk heartbeat for `checkHealth`; EPIPE is
|
|
259
|
+
swallowed without killing the process while non-EPIPE stream errors are
|
|
260
|
+
still surfaced; `unhandledRejection` consults live heartbeat stats rather
|
|
261
|
+
than killing a process mid-recovery.
|
|
262
|
+
- **Hostile-hub clamps** — 429 backoff capped at 30min so a malicious/buggy
|
|
263
|
+
`retry_after_ms: 86_400_000` can't silence the heartbeat for a day;
|
|
264
|
+
`unknown_node` anti-cache-poisoning backoff with an absolute deadline.
|
|
265
|
+
- Removed a dead wake hook in `evolver-signal-detect.js` whose relative
|
|
266
|
+
`require` was `MODULE_NOT_FOUND` in the deployed `.claude/hooks/` layout
|
|
267
|
+
(no in-proc consumer exists, so it was a no-op that cost a 3000-line
|
|
268
|
+
`require` on every IDE edit).
|
|
269
|
+
|
|
270
|
+
- **Standalone-binary obfuscation retries (#171).** Default `OBF_MAX_ATTEMPTS`
|
|
271
|
+
raised 4 → 12 in `scripts/build_binaries.js`. The obfuscation step
|
|
272
|
+
occasionally mangles `new.target` into an illegal `#target` token,
|
|
273
|
+
non-deterministically across node processes; the seed-perturbation retry is
|
|
274
|
+
the right mechanism but 4 attempts wasn't enough (the v1.87.4 deploy hit 4/4
|
|
275
|
+
and aborted before npm publish + binary upload). No behavior change on
|
|
276
|
+
first-attempt success; env override unchanged.
|
|
277
|
+
|
|
278
|
+
## [1.87.4] - 2026-06-02
|
|
279
|
+
|
|
280
|
+
### Fixed
|
|
281
|
+
|
|
282
|
+
- **Cursor hook compatibility: project-dir resolution, workspace-scoped
|
|
283
|
+
recall, FS workspace-id, and a non-git notice.** The session hooks assumed
|
|
284
|
+
`process.cwd()` was the user's project, but Cursor invokes some hook events
|
|
285
|
+
with cwd set to the plugin install dir — so `git diff` collection found
|
|
286
|
+
nothing and the session-end hook silently recorded every task as empty.
|
|
287
|
+
Hooks now resolve the workspace from `CURSOR_PROJECT_DIR` /
|
|
288
|
+
`CLAUDE_PROJECT_DIR` (falling back to cwd, a no-op on Codex/opencode/Kiro/CLI).
|
|
289
|
+
Session-start recall is now scoped to the current workspace *before* the
|
|
290
|
+
most-recent-N window (a shared user-level memory graph no longer leaks one
|
|
291
|
+
project's outcomes into another), with a bounded scan instead of parsing the
|
|
292
|
+
whole graph. `workspace_id` now resolves via an FS-only fallback when the
|
|
293
|
+
evolver package isn't reachable (plugin-only installs), writing the same
|
|
294
|
+
`<workspaceRoot>/.evolver/workspace-id` secret `paths.getWorkspaceId()` uses
|
|
295
|
+
so installing the package later is seamless. And a non-git folder now gets a
|
|
296
|
+
throttled one-line notice that evolution memory is inactive, instead of
|
|
297
|
+
failing silently. (Ported from public PRs #554/#555/#557/#558, each
|
|
298
|
+
Bugbot-reviewed; private-dev #169/#170.)
|
|
299
|
+
|
|
300
|
+
## [1.87.3] - 2026-06-01
|
|
301
|
+
|
|
302
|
+
### Changed
|
|
303
|
+
|
|
304
|
+
- **Router surfaces degenerate tier config + README defaults corrected
|
|
305
|
+
(#152).** The shipped `DEFAULT_TIER_MODELS` pins all three tiers to
|
|
306
|
+
`opus-4-7` on purpose (operators run tier-uniform while tuning tier
|
|
307
|
+
mapping, so the no-downgrade guard stays inert and 5xx retries replay one
|
|
308
|
+
model — PR #135). The trap is a user who flips `EVOMAP_ROUTER_ENABLED=1`
|
|
309
|
+
expecting the cost savings the README advertised but leaves the per-tier
|
|
310
|
+
`EVOMAP_MODEL_*` overrides unset: every turn resolves to the same model,
|
|
311
|
+
so routing is a silent no-op (and a cost *increase* for anyone previously
|
|
312
|
+
on a cheaper model). The proxy now emits a one-time `router_degenerate_tiers`
|
|
313
|
+
WARN at startup when the router is enabled and all three tiers resolve to a
|
|
314
|
+
single model. The README tier table previously documented `haiku-4-5` /
|
|
315
|
+
`sonnet-4-6` / `opus-4-7` as the defaults — that was the pre-#135 mapping
|
|
316
|
+
and never matched the shipped binary; it now states the real all-`opus-4-7`
|
|
317
|
+
defaults and how to set the overrides to get tier-based saving.
|
|
318
|
+
|
|
319
|
+
### Fixed
|
|
320
|
+
|
|
321
|
+
- **Heartbeat loop resilience (#544).** The heartbeat loop in
|
|
322
|
+
`LifecycleManager` had two coupled defects: (1) `heartbeat()` called
|
|
323
|
+
`store.countPending`, `getTaskMeta`, `_getEnvFingerprint`, and
|
|
324
|
+
`hello()` *before* its `try` block, so a single throw from any
|
|
325
|
+
helper (e.g. corrupt store, sandboxed `os.userInfo()`) escaped as a
|
|
326
|
+
rejected promise; (2) `tick()` had no try/catch around
|
|
327
|
+
`await this.heartbeat()`, so that rejection cancelled the next
|
|
328
|
+
`setTimeout` and silently killed the loop until the process
|
|
329
|
+
restarted. Reporter saw "first heartbeat fine, then evolver dead
|
|
330
|
+
after machine sleep — restart required". Fix wraps the entire
|
|
331
|
+
`heartbeat()` body in try/catch, defensively wraps the awaited call
|
|
332
|
+
in `_heartbeatTick()`, lowers the consecutive-failure backoff cap
|
|
333
|
+
from 30min → 15min (must stay above the 6min default interval or
|
|
334
|
+
exponential backoff inverts), and adds a generation counter so
|
|
335
|
+
`pokeHeartbeatLoop()` can't fork the loop when called while a tick
|
|
336
|
+
is mid-await. New `pokeHeartbeatLoop()` resets the schedule on
|
|
337
|
+
demand for callers that want wake-on-event semantics.
|
|
338
|
+
|
|
339
|
+
## [1.87.2] - 2026-05-27
|
|
340
|
+
|
|
341
|
+
### Added
|
|
342
|
+
|
|
343
|
+
- **ATP autoBuyer explicit consent surface (#141).** New
|
|
344
|
+
`evolver atp <enable|disable|status>` subcommands, an ack file at
|
|
345
|
+
`<memory>/atp-autobuy-ack.json`, and an env override
|
|
346
|
+
`EVOLVER_ATP_AUTOBUY=on|off`. Resolution order at runtime:
|
|
347
|
+
env > ack > default. `setConsent` writes are atomic (tmp + rename);
|
|
348
|
+
ack reads strictly validate `enabled: boolean` so a corrupted ack
|
|
349
|
+
cannot park a user in a "prompt suppressed + autoBuyer off" dead
|
|
350
|
+
zone. CLI enable/disable surfaces a loud WARN when an
|
|
351
|
+
`EVOLVER_ATP_AUTOBUY` env value would silently override the ack at
|
|
352
|
+
runtime. The cold-start `no_consent` daemon WARN sanitizes the hub
|
|
353
|
+
URL via `URL.origin` so basic-auth credentials in
|
|
354
|
+
`A2A_HUB_URL`/`EVOMAP_HUB_URL` are never written to logs.
|
|
355
|
+
|
|
356
|
+
### Changed
|
|
357
|
+
|
|
358
|
+
- **ATP autoBuyer default ON for new installs (#145, follow-up to
|
|
359
|
+
#141).** With no env override and no ack, `getConsent()` returns
|
|
360
|
+
`{enabled: true, source: 'default'}` and autoBuyer starts under the
|
|
361
|
+
daily/per-order caps (50/day, 10/order; cold-start half-cap for the
|
|
362
|
+
first 5 min). The first-run TTY prompt and `evolver atp disable` are
|
|
363
|
+
the explicit opt-out paths; ack='disable' continues to win over the
|
|
364
|
+
default. Daemon/hook/CI cold-start emits a one-time WARN naming the
|
|
365
|
+
hub origin, the active caps, and the `evolver atp disable` command,
|
|
366
|
+
so the policy is never silent.
|
|
367
|
+
|
|
368
|
+
- **`recallVerifier` default off (#136).** Client-side publish→recall
|
|
369
|
+
round-trip verification is no longer enabled by default. The Hub
|
|
370
|
+
`/a2a/fetch` contract is strict (unknown asset_id → empty results,
|
|
371
|
+
0 cost; verified against current Hub via random-hash smoke). The
|
|
372
|
+
round-trip check was redundant as a default safety net. Operators
|
|
373
|
+
who want end-to-end SLA observability can opt in with
|
|
374
|
+
`EVOLVE_RECALL_VERIFY=1`. No code paths removed; only the default
|
|
375
|
+
changed. (Already shipped in 1.87.1; promoted here for the
|
|
376
|
+
main-line changelog.)
|
|
377
|
+
|
|
378
|
+
### Fixed
|
|
379
|
+
|
|
380
|
+
- **Proxy router: canonicalize Bedrock model IDs + skip alias-only
|
|
381
|
+
5xx retry (#135) and defense-in-depth canonicalize at proxy
|
|
382
|
+
boundary (#139).** Resolves model alias drift between Anthropic-
|
|
383
|
+
shaped requests and Bedrock model IDs at both the router selection
|
|
384
|
+
layer and the inbound proxy boundary so a 5xx that originated from
|
|
385
|
+
an unresolvable alias is not retried as a different alias of the
|
|
386
|
+
same family.
|
|
387
|
+
|
|
388
|
+
- **`forceUpdate` preserves `.env`, `.env.local`, `USER.md`, and
|
|
389
|
+
`.evolver/` (#142).** Force-update no longer wipes operator
|
|
390
|
+
configuration or workspace identity files when overlaying a new
|
|
391
|
+
release on top of an existing install; the preserve-list is now
|
|
392
|
+
explicit and tested.
|
|
393
|
+
|
|
394
|
+
- **`evolver setup-hooks` ships `_memoryFiltering.js` alongside
|
|
395
|
+
generated session hooks (#547, #140, #143).** A fresh
|
|
396
|
+
`setup-hooks --platform=codex` against `@evomap/evolver@1.87.0`
|
|
397
|
+
left `.codex/hooks/` without `_memoryFiltering.js`, so the
|
|
398
|
+
generated `evolver-session-start.js` crashed immediately with
|
|
399
|
+
`Error: Cannot find module './_memoryFiltering'`.
|
|
400
|
+
`copyHookScripts`/`removeHookScripts` now include the helper, and
|
|
401
|
+
a regression suite scans every `require('./_*')` in the source
|
|
402
|
+
adapter scripts to assert install + uninstall cover them.
|
|
403
|
+
Reproduced by @rendigua on Windows; community PR
|
|
404
|
+
EvoMap/evolver#550 from @mvanhorn arrived independently with the
|
|
405
|
+
same diagnosis on the same day. (Already shipped in 1.87.1;
|
|
406
|
+
promoted here for the main-line changelog.)
|
|
407
|
+
|
|
408
|
+
### Docs
|
|
409
|
+
|
|
410
|
+
- **Backfill 1.87.0 proxy track + publish-gate entries
|
|
411
|
+
(#122/#123/#127/#128/#131/#132) into the changelog (#138).**
|
|
412
|
+
|
|
413
|
+
## [1.87.1] - 2026-05-27
|
|
414
|
+
|
|
415
|
+
Released from `release/1.87.1`; superseded by 1.87.2. Contains
|
|
416
|
+
#136 and #547/#140 (see above).
|
|
417
|
+
|
|
418
|
+
## [1.87.0] - 2026-05-26
|
|
419
|
+
|
|
420
|
+
### Added
|
|
421
|
+
|
|
422
|
+
- **AWS Bedrock as a second upstream for `/v1/messages` (#122).** Selected
|
|
423
|
+
per-request via `EVOMAP_UPSTREAM=bedrock` (default `anthropic`,
|
|
424
|
+
byte-for-byte unchanged). The Bedrock path pulls `body.model` →
|
|
425
|
+
`modelId`, injects `anthropic_version: bedrock-2023-05-31`, strips
|
|
426
|
+
top-level `model` / `stream` (Bedrock 400s on unknown fields, infers
|
|
427
|
+
stream from the command type), and re-emits Bedrock chunks as standard
|
|
428
|
+
SSE `data: <json>\n\n` so SDK callers don't need to know which upstream
|
|
429
|
+
served the request; the SDK owns SigV4 + AWS event-stream decoding.
|
|
430
|
+
`messages_route.js` softens the Anthropic-shaped credential check under
|
|
431
|
+
`EVOMAP_UPSTREAM=bedrock` (proxy's real gate is still the Bearer
|
|
432
|
+
`proxy_token` enforced in `ProxyHttpServer`; with SigV4 + AWS_* env in
|
|
433
|
+
place, inbound `x-api-key` is meaningless on the Bedrock path). This
|
|
434
|
+
unblocks Phase C cheap%-verification for deployments that don't have an
|
|
435
|
+
Anthropic-account upstream available — see PR #127 for the CC v2.1.150
|
|
436
|
+
body-shape compatibility patch that lands on top.
|
|
437
|
+
|
|
438
|
+
- **Optional OS-keychain backing for `workspace-id` (issue #111 Phase 1).**
|
|
439
|
+
`getWorkspaceId()` can now persist the per-workspace secret in the OS
|
|
440
|
+
keychain (`@napi-rs/keyring` optional dep — macOS Keychain Services /
|
|
441
|
+
libsecret on Linux / Windows Credential Manager) instead of leaving it
|
|
442
|
+
readable to any same-uid process via `<workspace>/.evolver/workspace-id`.
|
|
443
|
+
Mode is controlled by `EVOLVER_WORKSPACE_KEYCHAIN`:
|
|
444
|
+
- `auto` (default) — try keychain, fall back to FS on any failure.
|
|
445
|
+
Behaviour stays IDENTICAL to v1.85.x for any deployment that
|
|
446
|
+
doesn't install the optional addon.
|
|
447
|
+
- `force` — keychain only; throws if `@napi-rs/keyring` isn't loaded.
|
|
448
|
+
Use in CI to assert the addon is present.
|
|
449
|
+
- `off` — skip keychain; FS only.
|
|
450
|
+
When the addon is available, an existing FS secret is migrated into
|
|
451
|
+
the keychain on first call and the FS file is INTENTIONALLY retained
|
|
452
|
+
so bun-compiled binaries (which don't sideload the `.node` addon yet
|
|
453
|
+
— Phase 2) keep agreeing with node-CLI sessions on the same id.
|
|
454
|
+
`EVOLVER_WORKSPACE_ID` env override is unchanged and still wins.
|
|
455
|
+
|
|
456
|
+
### Fixed
|
|
457
|
+
|
|
458
|
+
- **Codex / Claude transcript directories are auto-discovered without
|
|
459
|
+
the `EVOLVER_CURSOR_TRANSCRIPTS_DIR` env (issue #543, PR #130 + #133).**
|
|
460
|
+
Before this release, `readCursorTranscripts()` returned empty unless
|
|
461
|
+
the operator had manually exported `EVOLVER_CURSOR_TRANSCRIPTS_DIR`,
|
|
462
|
+
so a fresh Codex install reported `[NO SESSION LOGS FOUND]` on every
|
|
463
|
+
cycle even though Codex was writing rollouts to `~/.codex/sessions/`.
|
|
464
|
+
`resolveTranscriptDirs()` now picks up `~/.codex/sessions/` and
|
|
465
|
+
`~/.claude/projects/` by default. Cross-project transcripts are
|
|
466
|
+
filtered against the current workspace so a session recorded in a
|
|
467
|
+
different repo can't leak into this review (Bugbot PR #130 Security
|
|
468
|
+
MEDIUM, fail-closed when `session_meta.cwd` is missing). PR #133
|
|
469
|
+
follow-up accepts multiple workspace identities (`WORKSPACE_ROOT`,
|
|
470
|
+
`process.cwd()`, `EVOLVER_REPO_ROOT`) so the auto-discovery still
|
|
471
|
+
works in a workspace whose cwd has no `.git` and `getRepoRoot()`
|
|
472
|
+
would otherwise fall back to the evolver install dir. Together with
|
|
473
|
+
the entry below, this closes the second half of issue #540 (#543's
|
|
474
|
+
half).
|
|
475
|
+
|
|
476
|
+
- **`user_missing` and `session_logs_missing` advisories now quiet down
|
|
477
|
+
on a Codex install whose `memory_graph.jsonl` is accumulating but has
|
|
478
|
+
no `outcome` records yet (issue #540 follow-up).** The original #540
|
|
479
|
+
fix landed a three-tier fallback for the memory snippet
|
|
480
|
+
(`MEMORY.md` → AGENTS.md marker section → memory_graph outcome tail),
|
|
481
|
+
but `readUserSnippet` and `_sessionLogFallback` still required
|
|
482
|
+
`_isOutcomeEntry`-shaped records to suppress their advisories. On a
|
|
483
|
+
fresh Codex install the first few cycles only write `signal` /
|
|
484
|
+
`hypothesis` / `attempt` / `epoch_boundary` entries — no
|
|
485
|
+
`outcome.status` — so both advisories kept firing on every cycle
|
|
486
|
+
even though the graph was visibly accumulating. Reported by
|
|
487
|
+
@rendigua on issue #540 with a 1.86.0 fresh-install reproduction.
|
|
488
|
+
The fix adds `readMemoryGraphAnyTail()` as a tier-2 fallback under
|
|
489
|
+
the existing outcome-tail tier, so any parseable graph entry is
|
|
490
|
+
enough to displace the legacy `[USER.md MISSING]` /
|
|
491
|
+
`[NO SESSION LOGS FOUND]` placeholders that `gep/signals.js`
|
|
492
|
+
(line 348-351) keys on. `readMemorySnippet`'s contract is unchanged
|
|
493
|
+
(AGENTS.md marker still wins over the graph for the memory tier).
|
|
494
|
+
PR #108 round-3 hardening (cross-workspace entries from the
|
|
495
|
+
user-level shared graph must pass the `workspace_id` / `cwd` filter)
|
|
496
|
+
applies to the new tier too.
|
|
497
|
+
|
|
498
|
+
- **Lifecycle `hello` no longer mints a fresh `node_id` when MailboxStore
|
|
499
|
+
is empty but `~/.evomap/node_id` still exists.** The legacy GEP path
|
|
500
|
+
(`src/gep/a2aProtocol.js`) writes the node_id as a bare hex file at
|
|
501
|
+
`~/.evomap/node_id`. Lifecycle's hello previously consulted only
|
|
502
|
+
`MailboxStore.getState('node_id')` and fell straight through to
|
|
503
|
+
`crypto.randomBytes(6)` when the store was unprimed. Any install whose
|
|
504
|
+
`state.json` was created (or wiped) after the legacy file existed —
|
|
505
|
+
e.g. an upgrade from a pre-lifecycle version, or a partial recovery
|
|
506
|
+
flow — therefore registered a brand-new A2ANode under the same owner
|
|
507
|
+
on the very next daemon boot, abandoning the original record (with
|
|
508
|
+
its stake / reputation / aliases) as an orphan in the web UI.
|
|
509
|
+
Reading `~/.evomap/node_id` between the store lookup and the random
|
|
510
|
+
fallback keeps both paths agreeing on a single identity.
|
|
511
|
+
|
|
512
|
+
- **Router refuses silent intra-family generational downgrades, and the
|
|
513
|
+
default tier IDs are now Bedrock-resolvable (#132).** Two related
|
|
514
|
+
router fixes that closed the same misconfiguration class. First, the
|
|
515
|
+
`/v1/messages` rewrite path now refuses moves where the chosen tier
|
|
516
|
+
model is strictly older than the caller's original within the same
|
|
517
|
+
Claude family (opus / sonnet / haiku) — cross-family rewrites (the
|
|
518
|
+
router's core cheap-tier behaviour, opus → haiku) stay allowed.
|
|
519
|
+
Triggered by the 2026-05-25 `/compact` stall on Aurora, where
|
|
520
|
+
`EVOMAP_MODEL_EXPENSIVE` was still pinned to `opus-4-1` while live
|
|
521
|
+
traffic had moved to `opus-4-7`: every planning turn silently
|
|
522
|
+
rewrote 4-7 → 4-1, hit Bedrock 5xx on the older endpoint, and
|
|
523
|
+
absorbed the cost via the upstream-5xx retry path. The guard makes
|
|
524
|
+
this **loud** (`router_fallback` with `reason: downgrade_blocked`)
|
|
525
|
+
instead of silent. Second, `DEFAULT_TIER_MODELS` previously used
|
|
526
|
+
short aliases that Bedrock rejects with `ValidationException: invalid
|
|
527
|
+
model identifier`. Defaults are now:
|
|
528
|
+
- `cheap = global.anthropic.claude-haiku-4-5-20251001-v1:0`
|
|
529
|
+
- `mid = global.anthropic.claude-sonnet-4-6` (only `global.*`
|
|
530
|
+
sonnet on Bedrock today)
|
|
531
|
+
- `expensive = global.anthropic.claude-opus-4-7`
|
|
532
|
+
The `mid = sonnet-4-6` choice is exactly the case the no-downgrade
|
|
533
|
+
guard protects: a caller pinned to `sonnet-4-7` does not silently
|
|
534
|
+
drop to 4-6 — the request stays on 4-7 and `downgrade_blocked` shows
|
|
535
|
+
up in telemetry, so when Bedrock adds a `global.*` `sonnet-4-7`
|
|
536
|
+
alias the regression becomes visible instead of invisible.
|
|
537
|
+
|
|
538
|
+
- **Claude Code v2.1.150+ body shapes accepted by the Bedrock upstream
|
|
539
|
+
(#127).** Three top-level fields CC v2.1.150 sends are accepted by
|
|
540
|
+
Anthropic's public API but rejected by Bedrock InvokeModel with HTTP
|
|
541
|
+
400 — `thinking: { type: "adaptive" }` (Bedrock only takes
|
|
542
|
+
`"enabled"` / `"disabled"`), `output_config: { effort }`, and
|
|
543
|
+
`context_management: { ... }` (both rejected as extra inputs). Without
|
|
544
|
+
this fix every CC v2.1.150 request through the proxy in Bedrock mode
|
|
545
|
+
400s, so the Phase C `EVOMAP_UPSTREAM=bedrock` data window introduced
|
|
546
|
+
by #122 could not actually log completed `router_decision` events.
|
|
547
|
+
`_proxyBedrock` now folds `thinking.type: "adaptive"` → `"enabled"`
|
|
548
|
+
(defaulting `budget_tokens` to `max_tokens / 2` with a 1024 floor when
|
|
549
|
+
CC omits it — adaptive mode lets the model pick, but Bedrock's
|
|
550
|
+
`"enabled"` requires the field) and strips `output_config` and
|
|
551
|
+
`context_management` before forwarding. The strip site sits next to
|
|
552
|
+
the existing `body.stream` strip and carries an explicit comment that
|
|
553
|
+
future CC schema additions will surface the same way (whole-request
|
|
554
|
+
400) and need to be added to the same list.
|
|
555
|
+
|
|
556
|
+
- **`proxy.token` reuses across daemon restarts instead of rotating on
|
|
557
|
+
every boot (#128).** `ProxyHttpServer.start()` previously called
|
|
558
|
+
`crypto.randomBytes(32)` unconditionally, so any long-lived shell that
|
|
559
|
+
had sourced `client-env.sh` once and was still exporting
|
|
560
|
+
`ANTHROPIC_AUTH_TOKEN` 401'd against the proxy as soon as the daemon
|
|
561
|
+
restarted. Aurora's typical mix (6+ `claude` processes across 4
|
|
562
|
+
Cursor terminal panes spawned hours before the most recent restart)
|
|
563
|
+
hit this on every daemon respawn. `start()` now reads `settings.json`
|
|
564
|
+
*before* `clearIfStale()` wipes the prior `proxy` block: if a token is
|
|
565
|
+
on disk, it's reused; a new one is minted only on first launch or
|
|
566
|
+
after a clean `stop()` (which still calls `clearSettings`). The trust
|
|
567
|
+
boundary is unchanged — the file remains mode 0600, the local
|
|
568
|
+
`127.0.0.1` socket is still reachable by any same-uid process, and
|
|
569
|
+
compromise recovery is `evox proxy stop` + `rm
|
|
570
|
+
~/.evolver/settings.json` + restart.
|
|
571
|
+
|
|
572
|
+
- **Grace-token list survives daemon restart (#131).** `_handleRequest`
|
|
573
|
+
now reads `proxy.previous_tokens` directly from `settings.json`
|
|
574
|
+
(constant-time compare against the primary plus the extras) and
|
|
575
|
+
`start()` preserves `previous_tokens` across restart instead of having
|
|
576
|
+
the shallow `writeSettings({proxy:{...}})` merge silently drop the
|
|
577
|
+
list. This replaces an earlier env-shim design
|
|
578
|
+
(`EVOMAP_PROXY_EXTRA_TOKENS` populated by a python3 block in
|
|
579
|
+
`evolver-daemon-start.sh`) that had three places to keep in sync.
|
|
580
|
+
Recovery path: when `~/.evolver/settings.json` is wiped externally
|
|
581
|
+
(logout / manual `rm`) while long-lived CC sessions still hold the
|
|
582
|
+
pre-wipe bearer in their fork-time env, the operator pastes the lost
|
|
583
|
+
token into `previous_tokens` and the proxy keeps accepting it until
|
|
584
|
+
those sessions die naturally. Clean `stop()` still calls
|
|
585
|
+
`clearSettings`, which intentionally drops the whole proxy block
|
|
586
|
+
including `previous_tokens` — clean shutdown wipes grace state by
|
|
587
|
+
design.
|
|
588
|
+
|
|
589
|
+
### Refactored
|
|
590
|
+
|
|
591
|
+
- **Lifecycle's legacy node_id reader now routes through
|
|
592
|
+
`paths.getEvomapPath()`** so `EVOLVER_HOME` honours both reader and
|
|
593
|
+
writer in lockstep (the writer in `src/gep/a2aProtocol.js` was
|
|
594
|
+
consolidated onto the same helper in #114). No behaviour change
|
|
595
|
+
beyond unifying the override semantics.
|
|
596
|
+
|
|
597
|
+
- **Seed genes opt three task classes into EvoX's
|
|
598
|
+
`GeneRoutingHint`.** PR #384 on the EvoX side wired
|
|
599
|
+
`Gene.routing_hint` through to the multi-tier router and reasoning
|
|
600
|
+
selector, but the bundled defaults shipped with `routing_hint: null`
|
|
601
|
+
for every gene — so `RouterDecision.reason="gene_hint"` never fired
|
|
602
|
+
in production telemetry (0 of 131 turns in the first reduction logged
|
|
603
|
+
in `evox/docs/concepts/router-lessons-learned-2026-05-18.md` §3).
|
|
604
|
+
This release bumps three seed genes off the default per the guidance
|
|
605
|
+
baked into the LLM prompt template (`src/gep/prompt.js:179-194`):
|
|
606
|
+
`gene_tool_integrity` and `gene_distilled_s2g-env-vars` go to
|
|
607
|
+
`cheap` + `low` (deterministic, mostly string/JSON manipulation), and
|
|
608
|
+
`gene_gep_optimize_tool_usage` goes to `mid` + `medium` (light
|
|
609
|
+
reasoning, no deep multi-step planning). The broader `repair` /
|
|
610
|
+
`innovate` genes are intentionally left null so the router's
|
|
611
|
+
classifier picks per-turn — matches the prompt's "Omit when the gene
|
|
612
|
+
applies broadly across complexities" rule. Fully additive; installs
|
|
613
|
+
whose local `genes.json` already overrides any of these IDs are not
|
|
614
|
+
touched (the asset_id-respecting load path in `loadGenes()` keeps
|
|
615
|
+
user customisations intact).
|
|
616
|
+
|
|
617
|
+
### Internal
|
|
618
|
+
|
|
619
|
+
- **End-to-end test for the issue #540 advisory-signal fix and a new
|
|
620
|
+
CHANGELOG release-section integrity guard (#113 / #115).** PR #105
|
|
621
|
+
added function-level tests for `readMemorySnippet` /
|
|
622
|
+
`readUserSnippet` / `readRealSessionLog`, but the user-visible bug
|
|
623
|
+
was the three advisory signals firing on every Codex review cycle
|
|
624
|
+
end-to-end. New tests run `collect.js` outputs through
|
|
625
|
+
`gep/signals.js` to lock in that pipeline so a future refactor
|
|
626
|
+
breaking the chain at either end fails loudly. The new
|
|
627
|
+
`scripts/check-changelog.js` + `pre_publish_check.js` integration
|
|
628
|
+
catches the #540 / PR #107 misattribution pattern (an entry filed
|
|
629
|
+
under `## [X.Y.Z]` after vX.Y.Z was already published). `extractSection`
|
|
630
|
+
is line-anchored so fenced code samples don't trigger false drift,
|
|
631
|
+
and `EVOLVER_CHANGELOG_GUARD_SOFT=1` is available as an interim
|
|
632
|
+
escape hatch while private-dev still relies on public-mirror tags.
|
|
633
|
+
|
|
634
|
+
- **Publish-time enforcement for the #542 duplicate-import regression
|
|
635
|
+
class (#123).** The regression test shipped with PR #118 (#542 hotfix)
|
|
636
|
+
was claimed to keep duplicate `const path = require('path')` from
|
|
637
|
+
reaching npm again. Auditing the gate showed the claim was incomplete:
|
|
638
|
+
`pre_publish_check.js` Check #1 (`npx vitest run`) is non-functional on
|
|
639
|
+
this project — every test file uses `require('node:test')`, so vitest
|
|
640
|
+
exits non-zero with `No test suite found` regardless of whether the
|
|
641
|
+
in-file tests pass, and publishers therefore set `SKIP_TESTS=1`. The
|
|
642
|
+
duplicate-import class would silently slip through that path. This
|
|
643
|
+
release adds `checkEntryPointScriptsParse`, a new pre-publish step
|
|
644
|
+
that runs `node --check` directly on every `.js` in
|
|
645
|
+
`src/adapters/scripts/` and on `index.js` (parse-only, ~50 ms per
|
|
646
|
+
file, independent of the test-suite check, names the offending file
|
|
647
|
+
and line in the failure message). It also collapses
|
|
648
|
+
`test/adaptersSyntax.test.js`'s dynamic per-file `it()` loop into one
|
|
649
|
+
static `it()` that iterates targets internally and reports every
|
|
650
|
+
failing file in a single assertion message — now statically
|
|
651
|
+
discoverable by both `node --test` and vitest's node:test compat
|
|
652
|
+
layer. Scoped narrowly to making the #542 class unpublishable; the
|
|
653
|
+
broader vitest-vs-node:test runner question is intentionally left
|
|
654
|
+
alone.
|
|
655
|
+
|
|
656
|
+
## [1.85.3] - 2026-05-23
|
|
657
|
+
|
|
658
|
+
### Fixed
|
|
659
|
+
|
|
660
|
+
- **Codex `session-end` hook no longer crashes with `SyntaxError: Identifier 'path' has already been declared` on fresh install.** A previous
|
|
661
|
+
change added a top-of-file import block (`fs` / `path` / `os`) without
|
|
662
|
+
noticing that an existing `const path = require('path')` further down
|
|
663
|
+
the file was still in place. Both v1.85.1 AND v1.85.2 shipped to npm
|
|
664
|
+
with this regression — every fresh `npm install -g @evomap/evolver`
|
|
665
|
+
followed by `evolver setup-hooks --platform=codex` produced a
|
|
666
|
+
parse-broken hook, blocking Codex `session-end` entirely with no
|
|
667
|
+
user-side workaround (issue #542). The duplicate is removed and a
|
|
668
|
+
`node --check` regression guard now runs against every file in
|
|
669
|
+
`src/adapters/scripts/` and the CLI entry as part of the test suite,
|
|
670
|
+
so this class of regression (parse-time failure in entry-point scripts
|
|
671
|
+
that the existing vitest suite never loads) cannot reach npm again.
|
|
672
|
+
|
|
673
|
+
- **`getRepoRoot()` no longer escapes `node_modules` to pick up an
|
|
674
|
+
unrelated outer `.git` (#541).** When `@evomap/evolver` is installed
|
|
675
|
+
globally (e.g. `npm install -g @evomap/evolver` on macOS Homebrew), the
|
|
676
|
+
package lives at `/opt/homebrew/lib/node_modules/@evomap/evolver` and
|
|
677
|
+
Homebrew itself is a git repository, so `/opt/homebrew/.git` exists.
|
|
678
|
+
Whenever the user ran `evolver` from a directory that did *not* have a
|
|
679
|
+
`.git` in any ancestor (a fresh project before `git init`, a generic
|
|
680
|
+
`~/` shell, an OpenClaw workspace, etc.), the upward walk from the
|
|
681
|
+
install location escaped the package's own `node_modules` and resolved
|
|
682
|
+
`repoRoot` to `/opt/homebrew` — silently sending `workspaceRoot` /
|
|
683
|
+
`memoryDir` / `evolutionDir` to a directory that doesn't belong to the
|
|
684
|
+
user, scanning the wrong session logs, and producing evolution
|
|
685
|
+
proposals for the wrong codebase. No crash; the only signal was that
|
|
686
|
+
the output looked subtly wrong. The walk now stops at the parent of
|
|
687
|
+
the nearest `node_modules` ancestor: for a local install
|
|
688
|
+
(`<project>/node_modules/@evomap/evolver`) the boundary still includes
|
|
689
|
+
`<project>` so the user's `.git` is found correctly; for a global
|
|
690
|
+
install the boundary is `/opt/homebrew/lib`, which has no `.git`, so
|
|
691
|
+
the walk falls back to the install dir instead of escaping. Dev
|
|
692
|
+
clones (not inside any `node_modules`) keep the original unbounded
|
|
693
|
+
walk. `EVOLVER_REPO_ROOT` remains the explicit override and is
|
|
694
|
+
unaffected.
|
|
695
|
+
|
|
696
|
+
## [1.85.2] - 2026-05-22
|
|
697
|
+
|
|
698
|
+
### Fixed
|
|
699
|
+
|
|
700
|
+
- **Daemon restart no longer overwrites the hub-rotated `node_secret` with a
|
|
701
|
+
stale shell `A2A_NODE_SECRET`.** When a previous run rotated the secret
|
|
702
|
+
via `/a2a/hello`, the hub-recognised value was persisted in
|
|
703
|
+
`~/.evomap/mailbox/state.json`. On the next daemon boot, the parent shell
|
|
704
|
+
still exported the *old* value of `A2A_NODE_SECRET` (a child process
|
|
705
|
+
cannot mutate its parent's env). The `_resolveNodeSecret` env-vs-store
|
|
706
|
+
reconciliation treated this as a conflict and let env win, silently
|
|
707
|
+
overwriting the rotated secret. Subsequent heartbeats 403'd, rotation
|
|
708
|
+
was correctly refused by the hub (issue #320 requires proof of current
|
|
709
|
+
ownership), and the daemon entered a 30-minute backoff that could not
|
|
710
|
+
be self-recovered. The store now carries a `node_secret_source` tag
|
|
711
|
+
(`'hub_rotate'` for hub-issued values, `'env_seed'` otherwise);
|
|
712
|
+
hub_rotate values win over a differing env, while #529's original
|
|
713
|
+
case (env-fresh, store-stale, no source tag) still falls back to env.
|
|
714
|
+
|
|
715
|
+
- **Re-auth backoff escalates exponentially.** Consecutive `reAuthenticate`
|
|
716
|
+
failures now back off 30min → 60min → 2h, capped at ~4h, instead of
|
|
717
|
+
resetting to a flat 30 minutes every time. A daemon stuck on a bad
|
|
718
|
+
secret no longer gets re-poked twice an hour by inbound auth errors,
|
|
719
|
+
drowning the log without surfacing the manual-recovery requirement.
|
|
720
|
+
|
|
721
|
+
### Added
|
|
722
|
+
|
|
723
|
+
- **`evolver reset-local-secret` CLI helper.** Wipes the three local
|
|
724
|
+
`node_secret` stores (MailboxStore, legacy `~/.evomap/node_secret`,
|
|
725
|
+
and prints an unset hint for the shell `A2A_NODE_SECRET`) so a daemon
|
|
726
|
+
whose hub side has been web-reset (`https://evomap.ai/account` →
|
|
727
|
+
Reset Secret) can boot clean. New `docs/secret-recovery.md` documents
|
|
728
|
+
the env-vs-store decision tree and the recovery flow.
|
|
729
|
+
|
|
730
|
+
## [1.85.1] - 2026-05-22
|
|
731
|
+
|
|
732
|
+
### Fixed
|
|
733
|
+
|
|
734
|
+
- **Stop hook no longer re-injects the evolution receipt as a user prompt.**
|
|
735
|
+
`evolver-session-end.js` previously emitted `followup_message`,
|
|
736
|
+
`stopMessage`, and `additionalContext` on every session end. The
|
|
737
|
+
`followup_message` field tells Claude Code to feed the receipt back into
|
|
738
|
+
the next inference round, which caused agents to "respond" to their own
|
|
739
|
+
evolution log line — visible to users as an unexplained extra reasoning
|
|
740
|
+
turn after every task. The hook now emits only `systemMessage`, a
|
|
741
|
+
UI-only notification that does not influence the next turn.
|
|
742
|
+
|
|
743
|
+
- **Cursor compatibility for the Stop hook.** Cursor's Claude Code-compatible
|
|
744
|
+
runtime currently treats `systemMessage` as a user prompt as well. The
|
|
745
|
+
hook now detects Cursor (via `TERM_PROGRAM=cursor`, `CURSOR_TRACE_ID`,
|
|
746
|
+
`CURSOR_SESSION_ID`, or the manual override `EVOLVER_HOOK_HOST=cursor`)
|
|
747
|
+
and omits `systemMessage` there. The receipt is always appended to
|
|
748
|
+
`~/.evolver/logs/evolution.log` (override path with
|
|
749
|
+
`EVOLVER_HOOK_LOG_DIR`) so it is never silently lost. Set
|
|
750
|
+
`EVOLVER_HOOK_VERBOSE=1` to force the inline notification on under
|
|
751
|
+
Cursor for debugging.
|
|
752
|
+
|
|
753
|
+
- **Stop hook process now exits promptly.** The hook held the event loop
|
|
754
|
+
open for ~7 s on every session end because its watchdog `setTimeout`
|
|
755
|
+
was never cleared after stdin closed. The watchdog is now cancelled and
|
|
756
|
+
the process exits explicitly when the response is written.
|
|
757
|
+
|
|
758
|
+
- **`evolver --review` no longer raises `memory_missing` / `user_missing` /
|
|
759
|
+
`session_logs_missing` on every cycle when running on Codex (#540).**
|
|
760
|
+
Codex doesn't generate a workspace-root `MEMORY.md` / `USER.md` and
|
|
761
|
+
doesn't expose readable session-transcript files, so the review
|
|
762
|
+
pipeline used to keep flagging those three advisory signals on every
|
|
763
|
+
cycle. `src/evolve/pipeline/collect.js` now falls back, in order:
|
|
764
|
+
`MEMORY.md` / `USER.md` → the `<!-- evolver-evolution-memory -->`
|
|
765
|
+
section that `setup-hooks` injects into the workspace's `AGENTS.md` /
|
|
766
|
+
`CLAUDE.md` → the tail of `memory_graph.jsonl` (last 5 outcomes).
|
|
767
|
+
`readRealSessionLog()` reuses the same memory-graph tail when no
|
|
768
|
+
platform session source has any data. Real markdown still wins, the
|
|
769
|
+
marked-section fallback is gated on the evolver marker (a
|
|
770
|
+
user-authored `AGENTS.md` without it is ignored), and the legacy
|
|
771
|
+
`[MEMORY.md MISSING]` / `[USER.md MISSING]` / `[NO SESSION LOGS
|
|
772
|
+
FOUND]` placeholders are still returned when nothing is available —
|
|
773
|
+
which transitively suppresses the three advisory signals that
|
|
774
|
+
`gep/signals.js` triggers on those literal strings. README adds a
|
|
775
|
+
"Codex caveats" subsection documenting the platform limitation.
|
|
776
|
+
|
|
777
|
+
### Security
|
|
778
|
+
|
|
779
|
+
- **Per-workspace random secret replaces plain-text `cwd` self-tag in
|
|
780
|
+
`memory_graph.jsonl` (#109).** PR #108 introduced a `cwd` field on
|
|
781
|
+
every memory-graph entry and used it to scope reads at the user-level
|
|
782
|
+
fallback path (`~/.evolver/memory/evolution/memory_graph.jsonl`),
|
|
783
|
+
closing the cross-workspace leak. The Cursor Bugbot round-3 advisory
|
|
784
|
+
pointed out that `cwd` is a self-report — any process under the same
|
|
785
|
+
uid could write entries claiming a different workspace's `cwd` and
|
|
786
|
+
poison its reads. This release adds `paths.getWorkspaceId()`, which
|
|
787
|
+
lazily creates `<workspace>/.evolver/workspace-id` (mode 0600,
|
|
788
|
+
32-hex random) and stamps every new entry with `workspace_id`. The
|
|
789
|
+
reader uses a three-tier policy: prefer `workspace_id` matching when
|
|
790
|
+
the current workspace has a secret, fall back to legacy `cwd`-only
|
|
791
|
+
matching when it doesn't (clean upgrade for pre-existing entries),
|
|
792
|
+
drop entries that have neither. The writer lazy-loads the canonical
|
|
793
|
+
resolver from the project-local evolver install so writer + reader
|
|
794
|
+
resolve from the same path. The secret file is created with
|
|
795
|
+
`O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW` after an `lstat`
|
|
796
|
+
pre-check, so a pre-placed symlink can't redirect the write outside
|
|
797
|
+
the workspace. `EVOLVER_WORKSPACE_ID` env var remains as an explicit
|
|
798
|
+
override / escape hatch. Note: in the co-uid threat model, any
|
|
799
|
+
process under the same uid can still *read* the FS secret —
|
|
800
|
+
migrating to an OS keychain is tracked in #111.
|
|
801
|
+
|
|
802
|
+
## [1.85.0] - 2026-05-22
|
|
803
|
+
|
|
804
|
+
### Fixed
|
|
805
|
+
|
|
806
|
+
- **Hook scripts now record outcomes to a writable location even when no
|
|
807
|
+
evolver-managed project directory is present (#536).** Previously,
|
|
808
|
+
npm-global installs of evolver had no `memory/evolution/` directory under
|
|
809
|
+
the install root, so `evolver-session-end.js` reported "nowhere (no Hub
|
|
810
|
+
or local path)" when neither `EVOMAP_HUB_URL` nor a project-local
|
|
811
|
+
evolver was configured. The shared `_runtimePaths.js` helper now falls
|
|
812
|
+
back to `~/.evolver/memory/evolution/memory_graph.jsonl`, creating the
|
|
813
|
+
directory on first write. The same helper teaches both
|
|
814
|
+
`evolver-session-start.js` and `evolver-session-end.js` to resolve the
|
|
815
|
+
package root via `require.resolve('@evomap/evolver/package.json')`,
|
|
816
|
+
which works for npm-global installs where the relative `../../..` walk
|
|
817
|
+
used to escape the package.
|
|
818
|
+
|
|
819
|
+
- **`evolver-session-end.js` no longer prints "system cannot find the
|
|
820
|
+
path" noise on Windows (#537).** The script now invokes git via
|
|
821
|
+
`spawnSync('git', [...], { shell: false })` with an argv array, which
|
|
822
|
+
avoids the POSIX `2>/dev/null` redirection that cmd.exe rejected. Failed
|
|
823
|
+
invocations (e.g. `HEAD~1` in a fresh repo) are detected by reading
|
|
824
|
+
`res.status` explicitly instead of relying on stderr suppression.
|
|
825
|
+
|
|
826
|
+
- **`setup-hooks --uninstall` now cleans up everything it installed
|
|
827
|
+
(#538).** Three regressions fixed:
|
|
828
|
+
- The Codex adapter now removes the `codex_hooks = true` line from
|
|
829
|
+
`~/.codex/config.toml` and drops the surrounding `[features]` block
|
|
830
|
+
when it becomes empty. Other unrelated entries under `[features]` are
|
|
831
|
+
preserved.
|
|
832
|
+
- All four markdown-injecting adapters (Claude Code, Codex, Kiro,
|
|
833
|
+
opencode) share a new `removeMarkedSection()` helper that correctly
|
|
834
|
+
skips the evolver-owned `## Evolution Memory` heading before searching
|
|
835
|
+
for the next H2. The previous inline implementations matched evolver's
|
|
836
|
+
own heading and left the entire injected section in CLAUDE.md /
|
|
837
|
+
AGENTS.md.
|
|
838
|
+
- `removeHookScripts()` now `console.warn`s when an `unlink` fails so
|
|
839
|
+
Windows file-locking and permission failures stop being silent.
|
|
840
|
+
- The Claude Code and Codex `uninstall` paths filter evolver-owned
|
|
841
|
+
entries by command match even when the `_evolver_managed` marker is
|
|
842
|
+
missing (older installs, hand-edited files), preventing stale
|
|
843
|
+
`evolver-session-*` entries from being stranded.
|
|
844
|
+
|
|
845
|
+
- **`setup-hooks` no longer overwrites existing user hooks under the same
|
|
846
|
+
event (#539).** The new `mergeWithHooksUnion()` keeps the user's own
|
|
847
|
+
Stop / SessionStart / PostToolUse entries while adding (or refreshing)
|
|
848
|
+
evolver-owned entries, identified by the command containing
|
|
849
|
+
`evolver-session` or `evolver-signal`. Reinstalls refresh the evolver
|
|
850
|
+
entry instead of duplicating it. This applies to both the matcher shape
|
|
851
|
+
used by Claude Code (`{matcher, hooks: [{type, command}]}`) and the
|
|
852
|
+
flat shape used by Codex (`{type, command}`).
|
|
853
|
+
|
|
854
|
+
### Removed
|
|
855
|
+
|
|
856
|
+
- **`FEISHU_EVOLVER_INTERVAL` env fallback for `pendingSleepMs` is no longer
|
|
857
|
+
read.** Part of removing all Feishu-specific naming from evolver core (#65).
|
|
858
|
+
Users who relied on this variable to override the pending-sleep interval
|
|
859
|
+
must switch to `EVOLVE_PENDING_SLEEP_MS` (or its older alias
|
|
860
|
+
`EVOLVE_MIN_INTERVAL`); both are still honored and have been the
|
|
861
|
+
documented names for this knob. Without an override, `pendingSleepMs`
|
|
862
|
+
now falls back to its existing 120000 ms default.
|
|
863
|
+
|
|
864
|
+
### Security
|
|
865
|
+
|
|
866
|
+
- **All Hub-facing HTTP calls now go through `hubFetch()`, a single
|
|
867
|
+
chokepoint that enforces both `https://` schema and TLS certificate
|
|
868
|
+
verification (C1 PR-2 Step 2).** `src/gep/hubFetch.js` rejects any URL
|
|
869
|
+
that does not parse as `https://` and passes an explicit
|
|
870
|
+
`undici.Agent({ connect: { rejectUnauthorized: true } })` as the request
|
|
871
|
+
dispatcher, making certificate verification immune to
|
|
872
|
+
`NODE_TLS_REJECT_UNAUTHORIZED=0`. Schema validation lives in `hubFetch`
|
|
873
|
+
itself (not only in `resolveHubUrl()`) because several hot paths —
|
|
874
|
+
`httpTransportSend`, `getHubUrl()`, per-call `opts.hubUrl`, proxy
|
|
875
|
+
`this.hubUrl` — bypass `resolveHubUrl()` and read env vars directly;
|
|
876
|
+
putting the check in the fetch wrapper means it cannot be bypassed.
|
|
877
|
+
Affected modules: `a2aProtocol`, `hubVerify`, `directoryClient`,
|
|
878
|
+
`hubReview`, `hubSearch`, `taskReceiver`, `skillPublisher`,
|
|
879
|
+
`validator/index`, `validator/reporter`, `validator/stakeBootstrap`,
|
|
880
|
+
`memoryGraph`, `proxy/lifecycle/manager`, `proxy/sync/inbound`,
|
|
881
|
+
`proxy/sync/outbound` (14 modules, 22 call sites). New runtime
|
|
882
|
+
dependency: `undici` (Node bundles it internally but its `Agent` is
|
|
883
|
+
not interface-compatible with the global `fetch` — both must come from
|
|
884
|
+
the same package, hence the explicit dep). Set
|
|
885
|
+
`EVOMAP_HUB_ALLOW_INSECURE=1` to bypass BOTH protections (local dev /
|
|
886
|
+
mock hub on http:// or self-signed cert; same gate as Step 1).
|
|
887
|
+
Integration test in `test/hubFetch.test.js` spins up a real HTTPS
|
|
888
|
+
server with a self-signed cert and asserts `hubFetch` rejects even
|
|
889
|
+
when `NODE_TLS_REJECT_UNAUTHORIZED=0` is set globally — proving the
|
|
890
|
+
documented attack is blocked end-to-end.
|
|
891
|
+
|
|
892
|
+
- **`resolveHubUrl()` now rejects non-`https://` Hub URLs (C1 PR-2 Step 1).**
|
|
893
|
+
Passing an `http://`, `ws://`, or unparseable string as `A2A_HUB_URL` /
|
|
894
|
+
`EVOMAP_HUB_URL` / `EVOLVER_DEFAULT_HUB_URL` now throws at call time rather
|
|
895
|
+
than silently returning a plaintext URL. This prevents a misconfigured or
|
|
896
|
+
attacker-controlled env var from downgrading heartbeat + capsule-publish
|
|
897
|
+
traffic to cleartext (MITM → arbitrary code injection via hub directives).
|
|
898
|
+
Local dev and mock-hub integration tests that need a non-TLS endpoint must
|
|
899
|
+
set `EVOMAP_HUB_ALLOW_INSECURE=1`; any value other than exactly `"1"` is
|
|
900
|
+
treated as absent. Do **not** use `NODE_ENV` for this gate — it is not stable
|
|
901
|
+
in `npm`-global installs.
|
|
902
|
+
|
|
903
|
+
### Changed
|
|
904
|
+
|
|
905
|
+
- **`--loop` daemon now defaults `EVOLVE_BRIDGE` to `'true'` (#96).**
|
|
906
|
+
The previous default of `'false'` made the daemon observe-only by
|
|
907
|
+
default: every cycle hit `rejectPendingRun(reason=loop_bridge_disabled_autoreject_no_rollback)`
|
|
908
|
+
and produced no `EvolutionEvent`. On Aurora this manifested as 33 days
|
|
909
|
+
of empty cycling. With the new default, the daemon actually applies
|
|
910
|
+
pending runs to the working tree. Failed cycles still recover safely
|
|
911
|
+
via `rollbackTracked` (mode=stash by default since v1.81.0): the
|
|
912
|
+
partial diff gets pushed to a stash entry the user can recover with
|
|
913
|
+
`git stash list | grep evolver-rollback` followed by `git stash pop`.
|
|
914
|
+
The daemon prints a safety banner on start documenting both the new
|
|
915
|
+
default and the recovery breadcrumb. Set `EVOLVE_BRIDGE=false`
|
|
916
|
+
explicitly to opt back into observe-only mode (the banner reflects
|
|
917
|
+
this choice as well). Operators upgrading should be aware that
|
|
918
|
+
`--loop` is no longer side-effect-free by default.
|
|
919
|
+
- **Daemon-mode `unhandledRejection` handler uses a sliding window (5
|
|
920
|
+
rejections within 5 minutes) instead of a cumulative process-lifetime
|
|
921
|
+
counter (#70).** Previously, four harmless rejections spread over days
|
|
922
|
+
plus a fifth unrelated one would terminate a long-running daemon for
|
|
923
|
+
noise; a cluster within a short window is the actual failure-cascade
|
|
924
|
+
signal. The stderr format also changed: each rejection log now reads
|
|
925
|
+
`Unhandled promise rejection (N in window):` and the exit line reads
|
|
926
|
+
`N unhandled rejections within 300s. Exiting...` — operators parsing
|
|
927
|
+
these lines should update their match patterns.
|
|
928
|
+
- **First-run `getNodeId()` fallback now generates a random ID instead of
|
|
929
|
+
hashing the device fingerprint (#71).** Previously, two installs cloned
|
|
930
|
+
from the same VM image before either had run evolver would compute
|
|
931
|
+
identical node IDs (same hostname / MAC / agent name / cwd → same
|
|
932
|
+
SHA-256). The new fallback emits 12 random hex characters; format and
|
|
933
|
+
persistence behaviour are unchanged. Existing installs already have a
|
|
934
|
+
persisted node ID from a prior run and never re-enter this path, so this
|
|
935
|
+
is not a migration. The startup warning text changed accordingly — any
|
|
936
|
+
log scraper matching `"Computing node ID from device fingerprint"`
|
|
937
|
+
should switch to `"Generating a fresh node ID"`.
|
|
938
|
+
- **`engines.node` tightened from `>=22` to `>=22.12`.** This release switches
|
|
939
|
+
`src/gep/contentHash.js` from an inlined SHA-256/canonicalize implementation
|
|
940
|
+
to a thin facade that `require()`s `@evomap/gep-sdk` (an ESM-only package).
|
|
941
|
+
`require()` of synchronous ESM was only unflagged in Node 22.12.0 (Dec
|
|
942
|
+
2024); on 22.0–22.11 the call throws `ERR_REQUIRE_ESM` without
|
|
943
|
+
`--experimental-require-module`. Users on those versions must upgrade Node
|
|
944
|
+
before installing `@evomap/evolver` 1.84.0+.
|
|
945
|
+
- **New runtime dependency: `@evomap/gep-sdk@^1.2.0`.** This package now owns
|
|
946
|
+
the GEP protocol surface (schemas, spec, `SCHEMA_VERSION`, `canonicalize`,
|
|
947
|
+
`computeAssetId`, `verifyAssetId`) for every implementation in the
|
|
948
|
+
ecosystem. Replaces four hand-maintained copies that drove the v1.80.8
|
|
949
|
+
`"explore"` enum drift incident. The 13 internal callsites that
|
|
950
|
+
`require('./contentHash')` keep working unchanged via a re-export facade.
|
|
951
|
+
- **`EVOLVER_ROLLBACK_MODE` default is now `stash` (was `hard`).** Previously a
|
|
952
|
+
failed solidify cycle ran `git reset --hard`, silently discarding uncommitted
|
|
953
|
+
work in the host repo. The `stash` branch always existed and was the safer
|
|
954
|
+
choice; only the default needed flipping. To restore the prior destructive
|
|
955
|
+
behaviour explicitly, set `EVOLVER_ROLLBACK_MODE=hard`.
|
|
956
|
+
|
|
957
|
+
### Fixed
|
|
958
|
+
|
|
959
|
+
- **`buildAutoGene` no longer emits evolver-internal validation commands when
|
|
960
|
+
running in non-evolver host repos.** Auto-generated genes used to embed
|
|
961
|
+
`node scripts/validate-modules.js …` and `node scripts/validate-suite.js`,
|
|
962
|
+
paths that only exist inside the evolver repo itself. In any third-party host
|
|
963
|
+
those scripts are missing, so validation always failed → soft-failure →
|
|
964
|
+
rollback discarded the user's working tree. New helper
|
|
965
|
+
`isInsideEvolverRepo(repoRoot)` (detected via `package.json` name
|
|
966
|
+
`@evomap/evolver`) gates this behaviour; in foreign repos validation falls
|
|
967
|
+
back to the portable `git diff --check`.
|
|
968
|
+
- **`rollbackNewUntrackedFiles` now skips files whose mtime predates the cycle
|
|
969
|
+
start.** The previous baseline-only filter could delete files the user
|
|
970
|
+
created in a parallel editor between baseline capture and rollback.
|
|
971
|
+
`cycleStartedAt` (sourced from `last_run.created_at`) is now passed through
|
|
972
|
+
from the solidify call site as an mtime guard. Behaviour is unchanged when
|
|
973
|
+
the parameter is omitted (legacy callers, unit tests).
|
|
974
|
+
|
|
975
|
+
## [1.80.7] - 2026-05-15
|
|
976
|
+
|
|
977
|
+
### Added
|
|
978
|
+
|
|
979
|
+
- **Formally declare Node.js >=22 engine requirement (`package.json`).** The
|
|
980
|
+
test suite uses `--test-isolation=process` (Node 22+). This field makes the
|
|
981
|
+
constraint explicit to package managers and CI environments so they surface
|
|
982
|
+
a clear error instead of a cryptic flag-not-found failure.
|
|
983
|
+
|
|
984
|
+
### Fixed
|
|
985
|
+
|
|
986
|
+
- **A2A inbound integrity filter: restore symmetric `asset_id` verification.**
|
|
987
|
+
`httpTransportReceive` was excluding the `a2a` field when recomputing the
|
|
988
|
+
`asset_id` of received assets, creating an asymmetry with the publish path
|
|
989
|
+
where `solidify` sets `capsule.a2a` *before* `buildPublishBundle` hashes the
|
|
990
|
+
asset. This caused every legitimately published capsule to fail the integrity
|
|
991
|
+
check and be silently discarded. The filter now calls `computeAssetId(asset)`
|
|
992
|
+
with only `asset_id` excluded (matching the publish-side default), and the
|
|
993
|
+
comment clarifies that `lowerConfidence` annotations are applied client-side
|
|
994
|
+
*after* verification, not by the Hub on stored assets.
|
|
995
|
+
|
|
996
|
+
- **GEP test isolation: scope `--test-isolation=process` to
|
|
997
|
+
`solidifyIntegration.test.js` only.** Previously the flag ran across all
|
|
998
|
+
test files, which doubled wall-clock time on CI for no benefit. Other test
|
|
999
|
+
files run in the same process and share setup cleanly.
|
|
1000
|
+
|
|
1001
|
+
- **HUB_DRY_RUN single source of truth.** Extracted `_isDryRun()` helper in
|
|
1002
|
+
`a2aProtocol.js`; `publishSkillChannel` in `skill2gep.js` now delegates to
|
|
1003
|
+
`a2a._isDryRun()` instead of duplicating the env-var check inline.
|
|
1004
|
+
|
|
1005
|
+
## [1.80.1] - 2026-05-07
|
|
1006
|
+
|
|
1007
|
+
### Fixed
|
|
1008
|
+
|
|
1009
|
+
- **Validator: stop flooding the Hub with `env_fail` reports when the local
|
|
1010
|
+
toolchain cannot run `node <script>` (issues #11, #15).** The validator
|
|
1011
|
+
daemon now runs a one-shot preflight self-test on startup
|
|
1012
|
+
(`runPreflight()` in `sandboxExecutor.js`) that writes a trivial script to
|
|
1013
|
+
the sandbox and confirms `spawn('node', [...])` exits with code 0. If the
|
|
1014
|
+
preflight fails (no `node` on PATH for headless invocations, missing exec
|
|
1015
|
+
perm, unwritable TMPDIR, ...) the validator role is **skipped** instead of
|
|
1016
|
+
posting `commands_passed=0, duration_ms=1` reports for every Hub-issued
|
|
1017
|
+
task. A user-visible warning is printed once with the diagnostic stderr
|
|
1018
|
+
tail so the operator can fix the host. Restart `evolver` after fixing PATH
|
|
1019
|
+
to re-enable the validator role. Validator preflight result is exposed via
|
|
1020
|
+
`getValidatorDaemonStats().preflight`.
|
|
1021
|
+
|
|
1022
|
+
### Added
|
|
1023
|
+
|
|
1024
|
+
- **Validator report diagnostics: per-command summaries + `failure_class`
|
|
1025
|
+
on every `ValidationReport` (issues #11, #15).** Each report now carries
|
|
1026
|
+
a top-level `failure_class` (one of `ok`, `parse_failed`,
|
|
1027
|
+
`executable_not_allowed`, `sandbox_block_node_flag`, `spawn_failed`,
|
|
1028
|
+
`timeout`, `exit_nonzero`, `unknown`) and a bounded `commands` array
|
|
1029
|
+
(capped at 8 entries) where each entry has `cmd`, `ok`, `exit_code`,
|
|
1030
|
+
`duration_ms`, `timed_out`, `failure_class`, and a 240-char `stderr_tail`.
|
|
1031
|
+
This lets the Hub-side classifier distinguish "Gene shipped legacy
|
|
1032
|
+
`node -e \"...\"` and the hardened sandbox rejected it" (a Hub/Gene
|
|
1033
|
+
incompatibility, route via `sandbox_block_node_flag`) from "validator
|
|
1034
|
+
host has no `node` binary" (genuine `env_fail`) instead of lumping both
|
|
1035
|
+
together. Older Hubs that ignore unknown payload fields keep working
|
|
1036
|
+
unchanged.
|
|
1037
|
+
|
|
1038
|
+
## [1.80.0] - 2026-05-07
|
|
1039
|
+
|
|
1040
|
+
### Added
|
|
1041
|
+
|
|
1042
|
+
- **First-party opencode adapter (issue #523).** `evolver setup-hooks
|
|
1043
|
+
--platform=opencode` now installs a managed plugin at
|
|
1044
|
+
`.opencode/plugins/evolver.js` plus the three evolver hook scripts in
|
|
1045
|
+
`.opencode/hooks/`. The plugin wires opencode's `session.created`,
|
|
1046
|
+
`session.idle`, and `tool.execute.after` events to the existing
|
|
1047
|
+
`evolver-session-start.js` / `evolver-session-end.js` /
|
|
1048
|
+
`evolver-signal-detect.js` filters, so the runtime behavior matches
|
|
1049
|
+
Cursor / Claude Code / Codex / Kiro.
|
|
1050
|
+
|
|
1051
|
+
Works with both project-level (`./.opencode/`) and user-level
|
|
1052
|
+
(`~/.opencode/`) installs. The plugin file is marked
|
|
1053
|
+
`_evolver_managed: true` on the first line so `--uninstall` only
|
|
1054
|
+
removes evolver-managed files and leaves user-authored plugins in
|
|
1055
|
+
`.opencode/plugins/` alone. Restart opencode after install to load
|
|
1056
|
+
the plugin.
|
|
1057
|
+
|
|
1058
|
+
See README for the platform table.
|
|
1059
|
+
|
|
1060
|
+
## [1.79.1] - 2026-05-06
|
|
1061
|
+
|
|
1062
|
+
### Fixed
|
|
1063
|
+
|
|
1064
|
+
- **Windows cmd-popup loop on suicide-respawn (issue #528).** On Windows,
|
|
1065
|
+
`child_process.spawn(detached: true, windowsHide: true)` allocates a new
|
|
1066
|
+
conhost (cmd) window every time -- `windowsHide` is silently ignored in
|
|
1067
|
+
detached mode, see Node.js child_process docs. So every time the daemon
|
|
1068
|
+
hit `EVOLVER_MAX_CYCLES` (default 100) or `EVOLVER_MAX_RSS_MB` (default
|
|
1069
|
+
500) and ran the suicide-respawn, Windows users saw a new cmd popup.
|
|
1070
|
+
v1.79.0 made this worse by adding a third spawn site for the cycle
|
|
1071
|
+
hard-timeout, copying the same buggy options.
|
|
1072
|
+
|
|
1073
|
+
The two in-process spawn sites are now consolidated into one helper
|
|
1074
|
+
`spawnReplacementProcess()` that, on Windows, defaults to *not*
|
|
1075
|
+
spawning. Instead the daemon `process.exit(1)`s and lets an external
|
|
1076
|
+
supervisor restart it. Compatible supervisors:
|
|
1077
|
+
|
|
1078
|
+
- NSSM (Non-Sucking Service Manager).
|
|
1079
|
+
- pm2-windows-startup.
|
|
1080
|
+
- Windows Task Scheduler with "On failure: restart" rule.
|
|
1081
|
+
|
|
1082
|
+
Users who explicitly want the in-process respawn (and accept the cmd
|
|
1083
|
+
popups) can opt back in with `EVOLVER_SUICIDE_WINDOWS=true`.
|
|
1084
|
+
|
|
1085
|
+
No behavior change on macOS/Linux.
|
|
1086
|
+
|
|
1087
|
+
### Notes on the original report
|
|
1088
|
+
|
|
1089
|
+
The reporter's auto-generated diagnosis attributed a separate symptom
|
|
1090
|
+
("`evolver buy/orders/publish` subcommands break the daemon") to lock
|
|
1091
|
+
contention on `evolver.pid`. That hypothesis is incorrect: subcommands
|
|
1092
|
+
do not call `acquireLock()` -- only `--loop` does. What the reporter
|
|
1093
|
+
observed was the same suicide-respawn cycle ending exactly when a
|
|
1094
|
+
subcommand happened to run, and the new conhost popup made it look like
|
|
1095
|
+
the subcommand caused it. Fixing the popup also fixes the perceived
|
|
1096
|
+
"daemon got killed" symptom.
|
|
1097
|
+
|
|
1098
|
+
## [1.79.0] - 2026-05-06
|
|
1099
|
+
|
|
1100
|
+
### Fixed
|
|
1101
|
+
|
|
1102
|
+
- **Cycle hard-timeout watchdog (issue #19).** The daemon main loop in
|
|
1103
|
+
`index.js` previously called `await evolve.run()` with no upper bound,
|
|
1104
|
+
so a single hung cycle (unclosed socket, stalled LLM call, etc.) could
|
|
1105
|
+
freeze the process indefinitely. One reporter observed cycle #5372
|
|
1106
|
+
stuck for 22 days at 0% CPU.
|
|
1107
|
+
|
|
1108
|
+
`evolve.run()` is now wrapped in `Promise.race(evolvePromise,
|
|
1109
|
+
cycleTimeoutPromise)`. When the timeout fires, the daemon emits a
|
|
1110
|
+
`[Daemon] Cycle hard-timeout exceeded after Nms (cycle=N, phase=Y)`
|
|
1111
|
+
diagnostic, force-spawns a replacement process, and exits with code 1
|
|
1112
|
+
so a host wrapper observes the dead pid.
|
|
1113
|
+
|
|
1114
|
+
New environment variables (both opt-out, default-on):
|
|
1115
|
+
|
|
1116
|
+
- `EVOLVER_CYCLE_TIMEOUT_ENABLED` (default `true`).
|
|
1117
|
+
- `EVOLVER_CYCLE_TIMEOUT_MS` (default `2700000`, i.e. 45 minutes).
|
|
1118
|
+
- `EVOLVER_PROGRESS_UPDATE_MS` (default `60000`).
|
|
1119
|
+
|
|
1120
|
+
### Added
|
|
1121
|
+
|
|
1122
|
+
- **`memory/evolution/cycle_progress.json` heartbeat.** The daemon now
|
|
1123
|
+
atomically writes a small JSON heartbeat at cycle start, every 60s
|
|
1124
|
+
while `evolve.run()` is in flight, and again at sleep entry. External
|
|
1125
|
+
watchdogs can poll `updated_at` to detect a true freeze (the file
|
|
1126
|
+
stays stale for >30 minutes only when the inner event loop is
|
|
1127
|
+
genuinely hung; normal long LLM cycles still refresh it via the
|
|
1128
|
+
60-second ticker).
|
|
1129
|
+
|
|
1130
|
+
Schema:
|
|
1131
|
+
|
|
1132
|
+
```json
|
|
1133
|
+
{ "pid": 12345, "outer_cycle": 5372, "inner_cycle": 17,
|
|
1134
|
+
"started_at": 1746543112000, "phase": "evolve.run|sleep|cycle_timeout_respawn",
|
|
1135
|
+
"updated_at": 1746543210000 }
|
|
1136
|
+
```
|
|
1137
|
+
|
|
1138
|
+
Regression tests: `test/cycleHardTimeout.test.js` (11 cases) and
|
|
1139
|
+
`test/cycleProgressFile.test.js` (4 cases).
|
|
1140
|
+
|
|
1141
|
+
|
|
1142
|
+
## [1.78.9] - 2026-05-04
|
|
1143
|
+
|
|
1144
|
+
### Fixed
|
|
1145
|
+
|
|
1146
|
+
- **`AGENT_SESSIONS_DIR` override silently ignored (issue #527).**
|
|
1147
|
+
`src/evolve.js` resolved the OpenClaw sessions directory at module
|
|
1148
|
+
load time with a hard-coded `os.homedir()/.openclaw/agents/<name>/sessions`
|
|
1149
|
+
path, bypassing `process.env.AGENT_SESSIONS_DIR` and
|
|
1150
|
+
`EVOLVER_SESSION_SCOPE`. On Windows and any non-standard OpenClaw
|
|
1151
|
+
layout (including `D:\openclaw\data\.openclaw\agents\...`), session
|
|
1152
|
+
logs were never read and every cycle surfaced
|
|
1153
|
+
`[NO SESSION LOGS FOUND]`, forcing the LLM to fall back to its own
|
|
1154
|
+
memory and appear to "cycle emptily".
|
|
1155
|
+
|
|
1156
|
+
The module-level `AGENT_SESSIONS_DIR` now delegates to
|
|
1157
|
+
`getAgentSessionsDir()` from `src/gep/paths.js`, which was already the
|
|
1158
|
+
intended single source of truth. `diagnoseSessionSourceEmpty()` reuses
|
|
1159
|
+
the same resolution when the caller does not inject a custom
|
|
1160
|
+
`homedir` / `agentName` pair, so diagnostic output now matches what
|
|
1161
|
+
the runtime actually reads.
|
|
1162
|
+
|
|
1163
|
+
`getAgentSessionsDir()` precedence, unchanged:
|
|
1164
|
+
1. `process.env.AGENT_SESSIONS_DIR` (explicit override)
|
|
1165
|
+
2. `workspace-<agent>` prefix in `EVOLVER_SESSION_SCOPE`
|
|
1166
|
+
3. `AGENT_NAME` (defaults to `main`)
|
|
1167
|
+
|
|
1168
|
+
Regression test: `test/evolveSessionsDir.test.js` (8 cases).
|
|
1169
|
+
|
|
1170
|
+
|
|
1171
|
+
## [1.78.8] - 2026-05-04
|
|
1172
|
+
|
|
1173
|
+
### Added
|
|
1174
|
+
|
|
1175
|
+
- **Built-in memory_graph.jsonl rotation (issue #519).** Long-running
|
|
1176
|
+
nodes previously accumulated multi-GB `memory/evolution/memory_graph.jsonl`
|
|
1177
|
+
files; one reporter observed 1.8 GB / 378k lines after 48h. Evolver now
|
|
1178
|
+
rotates the active file when it crosses a size threshold:
|
|
1179
|
+
- `EVOLVER_MEMORY_GRAPH_AUTO_ROTATE` (default `true`) enables rotation.
|
|
1180
|
+
- `EVOLVER_MEMORY_GRAPH_MAX_SIZE_MB` (default `100`) triggers rotation.
|
|
1181
|
+
- `EVOLVER_MEMORY_GRAPH_RETENTION_COUNT` (default `7`) caps how many
|
|
1182
|
+
rotated `memory_graph.jsonl.<ts>.gz` archives are kept on disk.
|
|
1183
|
+
- A startup pass rotates an already-oversized file on the next evolver
|
|
1184
|
+
start, so pre-existing giant files are handled automatically.
|
|
1185
|
+
- Rotation checks are throttled (once every ~30s or every 100 writes)
|
|
1186
|
+
so the write path stays cheap. Pruning and gzip compression run
|
|
1187
|
+
best-effort and never block the write path.
|
|
1188
|
+
- Regression test at `test/memoryGraphRotation.test.js`.
|
|
1189
|
+
|
|
1190
|
+
|
|
1191
|
+
## [1.78.7] - 2026-05-03
|
|
1192
|
+
|
|
1193
|
+
### Fixed
|
|
1194
|
+
|
|
1195
|
+
- **`EVOLVER_REPO_ROOT` set in `.env` is now honored (chicken-and-egg fix).**
|
|
1196
|
+
`index.js` previously called `getRepoRoot()` to locate the `.env` file
|
|
1197
|
+
before `dotenv` had a chance to populate `EVOLVER_REPO_ROOT` from that
|
|
1198
|
+
very file, and `getRepoRoot()` cached the `.git`-walk result on first
|
|
1199
|
+
call -- so any `EVOLVER_REPO_ROOT` declared in `.env` was silently
|
|
1200
|
+
ignored and evolver ran against the wrong repository (often
|
|
1201
|
+
`node_modules/@evomap/evolver` itself on local npm installs). Fix:
|
|
1202
|
+
- Bootstrap now loads `.env` from `process.cwd()` first (independent
|
|
1203
|
+
of any cache) so an `EVOLVER_REPO_ROOT` declared there takes effect
|
|
1204
|
+
immediately.
|
|
1205
|
+
- `getRepoRoot()` re-reads `process.env.EVOLVER_REPO_ROOT` on every
|
|
1206
|
+
call and overrides the cache when the env var is set, so any
|
|
1207
|
+
override populated by a later `.env` load propagates cleanly.
|
|
1208
|
+
- Regression test at `test/paths.test.js` covers the "env set AFTER
|
|
1209
|
+
first getRepoRoot call" path. Reported in #526.
|
|
1210
|
+
|
|
1211
|
+
|
|
1212
|
+
## [1.78.1] - 2026-05-02
|
|
1213
|
+
|
|
1214
|
+
### Fixed
|
|
1215
|
+
|
|
1216
|
+
- **Capsule publish payloads now attach a hub-shape `execution_trace` array**
|
|
1217
|
+
(`src/gep/solidify.js`). Previously the trace object was only written onto
|
|
1218
|
+
the sibling `EvolutionEvent`; the `Capsule` itself went out with no trace,
|
|
1219
|
+
so every Capsule on the hub was flagged `trace_empty` and triggered the
|
|
1220
|
+
`capsule-trace-enforce] would-reject` alert stream (~530 rejects / 15 min
|
|
1221
|
+
in prod). The array form is synthesized from the existing
|
|
1222
|
+
`validation.results`, `blast` and `canary` data -- no new data capture --
|
|
1223
|
+
with each step carrying `stage` + `cmd` + `exit` to satisfy
|
|
1224
|
+
`capsuleTraceQualityService`'s shape check. Anti-pattern publishes
|
|
1225
|
+
(`EVOLVER_PUBLISH_ANTI_PATTERNS=true`) get the same treatment.
|
|
1226
|
+
|
|
1227
|
+
### Internal
|
|
1228
|
+
|
|
1229
|
+
- New exported helper `buildCapsuleTraceSteps(...)` in `src/gep/solidify.js`
|
|
1230
|
+
with a regression test at `test/capsuleExecutionTrace.test.js` that
|
|
1231
|
+
replicates the hub's shape gates, so a drift on either end breaks CI before
|
|
1232
|
+
it inflates production alerts.
|
|
1233
|
+
|
|
1234
|
+
## [1.78.0] - earlier
|
|
1235
|
+
|
|
1236
|
+
- `evolver sync --scope` and `--export .gepx` for full-account asset
|
|
1237
|
+
downloads. See `618e451`.
|