@evomap/evolver 1.89.12 → 1.89.13

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