@evomap/evolver 1.90.0 → 2.0.0-beta.1

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 (231) hide show
  1. package/README.md +27 -564
  2. package/bin/evolver-llm-proxy.js +3 -0
  3. package/bin/evolver-mcp.js +2 -0
  4. package/bin/evolver-proxy.js +3 -0
  5. package/bin/evolver.js +4 -0
  6. package/index.js +1 -3670
  7. package/package.json +32 -62
  8. package/CONTRIBUTING.md +0 -19
  9. package/LICENSE +0 -641
  10. package/README.ja-JP.md +0 -521
  11. package/README.ko-KR.md +0 -520
  12. package/README.zh-CN.md +0 -531
  13. package/SKILL.md +0 -365
  14. package/assets/cover.png +0 -0
  15. package/assets/gep/genes.seed.json +0 -496
  16. package/conformance/savings-core/constants.json +0 -30
  17. package/conformance/savings-core/golden-vectors.json +0 -333
  18. package/scripts/a2a_export.js +0 -63
  19. package/scripts/a2a_ingest.js +0 -79
  20. package/scripts/a2a_promote.js +0 -118
  21. package/scripts/analyze_by_skill.js +0 -121
  22. package/scripts/build_binaries.js +0 -479
  23. package/scripts/check-changelog.js +0 -166
  24. package/scripts/extract_log.js +0 -85
  25. package/scripts/generate_history.js +0 -75
  26. package/scripts/gep_append_event.js +0 -96
  27. package/scripts/gep_personality_report.js +0 -234
  28. package/scripts/human_report.js +0 -147
  29. package/scripts/recall-verify-report.js +0 -234
  30. package/scripts/recover_loop.js +0 -61
  31. package/scripts/refresh_stars_badge.js +0 -168
  32. package/scripts/seed-merchants.js +0 -91
  33. package/scripts/skill2recipes.js +0 -118
  34. package/scripts/suggest_version.js +0 -89
  35. package/scripts/validate-modules.js +0 -38
  36. package/scripts/validate-suite.js +0 -78
  37. package/src/adapters/claudeCode.js +0 -194
  38. package/src/adapters/codex.js +0 -216
  39. package/src/adapters/cursor.js +0 -91
  40. package/src/adapters/hookAdapter.js +0 -469
  41. package/src/adapters/kiro.js +0 -195
  42. package/src/adapters/opencode.js +0 -326
  43. package/src/adapters/scripts/_lockPaths.js +0 -74
  44. package/src/adapters/scripts/_memoryFiltering.js +0 -35
  45. package/src/adapters/scripts/_runtimePaths.js +0 -440
  46. package/src/adapters/scripts/evolver-session-end.js +0 -321
  47. package/src/adapters/scripts/evolver-session-start.js +0 -587
  48. package/src/adapters/scripts/evolver-signal-detect.js +0 -98
  49. package/src/adapters/scripts/evolver-task-recall.js +0 -173
  50. package/src/atp/atpExecute.js +0 -283
  51. package/src/atp/atpTaskPickup.js +0 -233
  52. package/src/atp/autoBuyer.js +0 -382
  53. package/src/atp/autoDeliver.js +0 -215
  54. package/src/atp/cli.js +0 -354
  55. package/src/atp/cliAutobuyPrompt.js +0 -154
  56. package/src/atp/consumerAgent.js +0 -157
  57. package/src/atp/defaultHandler.js +0 -69
  58. package/src/atp/heartbeatSignalsHandler.js +0 -254
  59. package/src/atp/hubClient.js +0 -317
  60. package/src/atp/index.js +0 -38
  61. package/src/atp/merchantAgent.js +0 -118
  62. package/src/atp/protocol.js +0 -41
  63. package/src/atp/questionComposer.js +0 -133
  64. package/src/atp/serviceHelper.js +0 -92
  65. package/src/canary.js +0 -13
  66. package/src/config.js +0 -351
  67. package/src/evolve/guards.js +0 -1
  68. package/src/evolve/pipeline/collect.js +0 -1
  69. package/src/evolve/pipeline/dispatch.js +0 -1
  70. package/src/evolve/pipeline/enrich.js +0 -1
  71. package/src/evolve/pipeline/hub.js +0 -1
  72. package/src/evolve/pipeline/select.js +0 -1
  73. package/src/evolve/pipeline/signals.js +0 -1
  74. package/src/evolve/utils.js +0 -1
  75. package/src/evolve.js +0 -1
  76. package/src/experiment/agentRunner.js +0 -229
  77. package/src/experiment/cli.js +0 -159
  78. package/src/experiment/comparison.js +0 -233
  79. package/src/experiment/metrics.js +0 -75
  80. package/src/forceUpdate.js +0 -991
  81. package/src/gep/a2a.js +0 -173
  82. package/src/gep/a2aProtocol.js +0 -1
  83. package/src/gep/analyzer.js +0 -35
  84. package/src/gep/antiAbuseTelemetry.js +0 -1
  85. package/src/gep/assetCallLog.js +0 -197
  86. package/src/gep/assetStore.js +0 -723
  87. package/src/gep/assets.js +0 -36
  88. package/src/gep/autoDistillConv.js +0 -1
  89. package/src/gep/autoDistillLlm.js +0 -1
  90. package/src/gep/bridge.js +0 -138
  91. package/src/gep/candidateEval.js +0 -1
  92. package/src/gep/candidates.js +0 -1
  93. package/src/gep/claimNudge.js +0 -121
  94. package/src/gep/cliContracts.js +0 -1190
  95. package/src/gep/contentHash.js +0 -1
  96. package/src/gep/conversationDistiller.js +0 -1
  97. package/src/gep/conversationSniffer.js +0 -1
  98. package/src/gep/crypto.js +0 -1
  99. package/src/gep/curriculum.js +0 -1
  100. package/src/gep/deviceId.js +0 -1
  101. package/src/gep/directoryClient.js +0 -115
  102. package/src/gep/envFingerprint.js +0 -1
  103. package/src/gep/epigenetics.js +0 -1
  104. package/src/gep/execBridge.js +0 -1
  105. package/src/gep/executionTrace.js +0 -291
  106. package/src/gep/explore.js +0 -1
  107. package/src/gep/featureFlags.js +0 -121
  108. package/src/gep/gitOps.js +0 -265
  109. package/src/gep/hash.js +0 -1
  110. package/src/gep/hostErrorClassifier.js +0 -34
  111. package/src/gep/hubFetch.js +0 -1
  112. package/src/gep/hubReview.js +0 -1
  113. package/src/gep/hubSearch.js +0 -1
  114. package/src/gep/hubVerify.js +0 -1
  115. package/src/gep/idleScheduler.js +0 -400
  116. package/src/gep/issueReporter.js +0 -416
  117. package/src/gep/learningSignals.js +0 -1
  118. package/src/gep/llmReview.js +0 -92
  119. package/src/gep/localStateAwareness.js +0 -243
  120. package/src/gep/mailboxTransport.js +0 -119
  121. package/src/gep/memoryGraph.js +0 -1
  122. package/src/gep/memoryGraphAdapter.js +0 -1
  123. package/src/gep/mutation.js +0 -1
  124. package/src/gep/narrativeMemory.js +0 -1
  125. package/src/gep/oauthLogin.js +0 -181
  126. package/src/gep/openPRRegistry.js +0 -1
  127. package/src/gep/paths.js +0 -522
  128. package/src/gep/personality.js +0 -1
  129. package/src/gep/policyCheck.js +0 -1
  130. package/src/gep/portable.js +0 -103
  131. package/src/gep/privacyClient.js +0 -235
  132. package/src/gep/prompt.js +0 -1
  133. package/src/gep/questionGenerator.js +0 -518
  134. package/src/gep/recallInject.js +0 -1
  135. package/src/gep/recallVerifier.js +0 -1
  136. package/src/gep/reflection.js +0 -1
  137. package/src/gep/sanitize.js +0 -264
  138. package/src/gep/savingsCore.js +0 -1
  139. package/src/gep/schemas/capsule.js +0 -170
  140. package/src/gep/schemas/gene.js +0 -154
  141. package/src/gep/schemas/index.js +0 -8
  142. package/src/gep/schemas/protocol.js +0 -51
  143. package/src/gep/schemas/task.js +0 -74
  144. package/src/gep/selector.js +0 -1
  145. package/src/gep/selfPR.js +0 -469
  146. package/src/gep/signals.js +0 -776
  147. package/src/gep/skill2gep.js +0 -1056
  148. package/src/gep/skill2gepAudit.js +0 -303
  149. package/src/gep/skill2recipes.js +0 -511
  150. package/src/gep/skillDistiller.js +0 -1
  151. package/src/gep/skillPublisher.js +0 -358
  152. package/src/gep/solidify.js +0 -1
  153. package/src/gep/strategy.js +0 -1
  154. package/src/gep/taskReceiver.js +0 -575
  155. package/src/gep/tokenSavings.js +0 -1
  156. package/src/gep/trajectoryExport.js +0 -1
  157. package/src/gep/validationReport.js +0 -55
  158. package/src/gep/validator/index.js +0 -411
  159. package/src/gep/validator/reporter.js +0 -210
  160. package/src/gep/validator/sandboxExecutor.js +0 -480
  161. package/src/gep/validator/stakeBootstrap.js +0 -357
  162. package/src/gep/workspaceKeychain.js +0 -1
  163. package/src/ops/cleanup.js +0 -80
  164. package/src/ops/commentary.js +0 -60
  165. package/src/ops/health_check.js +0 -104
  166. package/src/ops/index.js +0 -11
  167. package/src/ops/innovation.js +0 -67
  168. package/src/ops/lifecycle.js +0 -798
  169. package/src/ops/self_repair.js +0 -76
  170. package/src/ops/skills_monitor.js +0 -147
  171. package/src/ops/trigger.js +0 -33
  172. package/src/proxy/clientSettings.js +0 -405
  173. package/src/proxy/envelope.js +0 -59
  174. package/src/proxy/extensions/dmHandler.js +0 -45
  175. package/src/proxy/extensions/sessionHandler.js +0 -141
  176. package/src/proxy/extensions/skillUpdater.js +0 -64
  177. package/src/proxy/extensions/traceControl.js +0 -1
  178. package/src/proxy/index.js +0 -1395
  179. package/src/proxy/inject.js +0 -1
  180. package/src/proxy/lifecycle/manager.js +0 -1568
  181. package/src/proxy/mailbox/state.js +0 -207
  182. package/src/proxy/mailbox/store.js +0 -602
  183. package/src/proxy/router/cache_passthrough.js +0 -26
  184. package/src/proxy/router/features.js +0 -84
  185. package/src/proxy/router/gemini_route.js +0 -154
  186. package/src/proxy/router/messages_route.js +0 -535
  187. package/src/proxy/router/model_router.js +0 -113
  188. package/src/proxy/router/models_route.js +0 -52
  189. package/src/proxy/router/ollama_route.js +0 -103
  190. package/src/proxy/router/responses_route.js +0 -170
  191. package/src/proxy/router/vertex_route.js +0 -110
  192. package/src/proxy/server/http.js +0 -363
  193. package/src/proxy/server/routes.js +0 -558
  194. package/src/proxy/server/settings.js +0 -115
  195. package/src/proxy/sync/engine.js +0 -179
  196. package/src/proxy/sync/inbound.js +0 -211
  197. package/src/proxy/sync/outbound.js +0 -361
  198. package/src/proxy/task/monitor.js +0 -131
  199. package/src/proxy/trace/extractor.js +0 -1
  200. package/src/proxy/trace/usage.js +0 -1
  201. package/src/solo/breaker.js +0 -25
  202. package/src/solo/gitGuard.js +0 -65
  203. package/src/webui/client/clientJs/assets.js +0 -111
  204. package/src/webui/client/clientJs/bootstrap.js +0 -92
  205. package/src/webui/client/clientJs/common.js +0 -77
  206. package/src/webui/client/clientJs/i18n.js +0 -366
  207. package/src/webui/client/clientJs/index.js +0 -35
  208. package/src/webui/client/clientJs/interactions.js +0 -351
  209. package/src/webui/client/clientJs/overview.js +0 -152
  210. package/src/webui/client/clientJs/personality.js +0 -285
  211. package/src/webui/client/clientJs/pipelines.js +0 -330
  212. package/src/webui/client/indexHtml.js +0 -221
  213. package/src/webui/client/static.js +0 -23
  214. package/src/webui/client/stylesCss.js +0 -639
  215. package/src/webui/client/vendor/README.md +0 -15
  216. package/src/webui/client/vendor/echarts.min.js +0 -45
  217. package/src/webui/index.js +0 -14
  218. package/src/webui/observer/assets.js +0 -146
  219. package/src/webui/observer/index.js +0 -37
  220. package/src/webui/observer/interactions.js +0 -127
  221. package/src/webui/observer/jsonl.js +0 -75
  222. package/src/webui/observer/paths.js +0 -46
  223. package/src/webui/observer/personality.js +0 -43
  224. package/src/webui/observer/pipelineEvents.js +0 -58
  225. package/src/webui/observer/redact.js +0 -63
  226. package/src/webui/observer/runs.js +0 -356
  227. package/src/webui/observer/safety.js +0 -57
  228. package/src/webui/observer/skills.js +0 -70
  229. package/src/webui/observer/status.js +0 -71
  230. package/src/webui/server/http.js +0 -138
  231. package/src/webui/server/routes.js +0 -41
@@ -1,321 +0,0 @@
1
- #!/usr/bin/env node
2
- // evolver-session-end.js
3
- // Records evolution outcome at session end.
4
- // Collects git diff stats, extracts signals, records via Hub API or local memory.
5
- // Input: stdin JSON. Output: stdout JSON with `systemMessage` (Claude Code Stop
6
- // hook notification) — or empty `{}` on Cursor where systemMessage is mishandled.
7
-
8
- const fs = require('fs');
9
- const path = require('path');
10
- const os = require('os');
11
- const { spawnSync } = require('child_process');
12
- // 10 MB — prevents RangeError on large child process output (e.g. git log/diff
13
- // on large repos). See GHSA reports / issue #451.
14
- const MAX_EXEC_BUFFER = 10 * 1024 * 1024;
15
-
16
- const { findEvolverRoot, findMemoryGraph, resolveProjectDir, resolveWorkspaceId } = require('./_runtimePaths');
17
-
18
- // Workspace-id resolution is shared with the session-start reader via
19
- // _runtimePaths.resolveWorkspaceId(). Reader and writer MUST resolve the SAME
20
- // id or workspace scoping silently breaks (no entry would ever match the
21
- // reader's filter), so this logic lives in exactly one place instead of being
22
- // duplicated here. The shared resolver mirrors src/gep/paths.js#getWorkspaceId()
23
- // loaded from the evolver root, with an EVOLVER_WORKSPACE_ID env override —
24
- // consistent with the review-time reader in src/evolve/pipeline/collect.js
25
- // (Bugbot PR #109 round-1 MEDIUM; reader/writer drift flagged on PR #555).
26
-
27
- function runGit(args, cwd) {
28
- // Argv-array form, no shell. Avoids POSIX `2>/dev/null` redirects that
29
- // break on Windows cmd.exe (#537). Failures (e.g. no HEAD~1 in a fresh
30
- // repo) are surfaced as a non-zero status; callers distinguish them
31
- // from successful empty output via the `ok` flag (PR #94 round-6 LOW).
32
- const res = spawnSync('git', args, {
33
- cwd,
34
- encoding: 'utf8',
35
- timeout: 5000,
36
- maxBuffer: MAX_EXEC_BUFFER,
37
- stdio: ['ignore', 'pipe', 'pipe'],
38
- shell: false,
39
- windowsHide: true,
40
- });
41
- if (res.status === 0 && typeof res.stdout === 'string') {
42
- return { ok: true, out: res.stdout.trim() };
43
- }
44
- return { ok: false, out: '' };
45
- }
46
-
47
- function getGitDiffStats() {
48
- // Use the host-provided workspace root, not process.cwd(): Cursor runs some
49
- // hook events with cwd set to the plugin dir, where `git diff` finds nothing.
50
- const cwd = resolveProjectDir();
51
- // Distinguish "git failed (no HEAD~1, etc.)" from "git succeeded with
52
- // empty output (e.g. empty merge)". The previous `||` chain treated
53
- // both as falsy and fell through to the working-tree diff, which can
54
- // surface unrelated unstaged changes as the session outcome.
55
- const statHead1 = runGit(['diff', '--stat', 'HEAD~1'], cwd);
56
- const stat = statHead1.ok ? statHead1.out : runGit(['diff', '--stat'], cwd).out;
57
- const diffHead1 = runGit(['diff', '--no-color', 'HEAD~1'], cwd);
58
- const diffContent = diffHead1.ok ? diffHead1.out : runGit(['diff', '--no-color'], cwd).out;
59
- const filesChanged = (stat.match(/\d+ files? changed/) || ['0'])[0];
60
- const insertions = (stat.match(/(\d+) insertions?/) || [null, '0'])[1];
61
- const deletions = (stat.match(/(\d+) deletions?/) || [null, '0'])[1];
62
- // Distinguish "no git repo here" from "repo with no changes" purely for the
63
- // skip-log message — the diff commands above can't tell the two apart (both
64
- // yield empty output). A single cheap rev-parse settles it.
65
- const isRepo = runGit(['rev-parse', '--is-inside-work-tree'], cwd).out === 'true';
66
- return {
67
- stat,
68
- summary: `${filesChanged}, +${insertions}/-${deletions}`,
69
- diffSnippet: diffContent.slice(0, 2000),
70
- hasChanges: stat.length > 0,
71
- isRepo,
72
- };
73
- }
74
-
75
- // Detect whether the hook is running inside Cursor.
76
- //
77
- // Why: Claude Code's Stop hook spec says `systemMessage` is a notification
78
- // shown to the user and is NOT fed back into Claude's context. Cursor's
79
- // Claude Code-compatible runtime currently splices it into the next
80
- // inference round as if it were a user prompt, so Claude "responds" to the
81
- // evolution receipt — visible to users as an unexplained extra reasoning
82
- // turn after every task. Until Cursor fixes this, suppress systemMessage
83
- // on Cursor while keeping the local-memory append intact.
84
- //
85
- // Detection (any of):
86
- // - TERM_PROGRAM=cursor
87
- // - CURSOR_TRACE_ID / CURSOR_SESSION_ID set
88
- // - EVOLVER_HOOK_HOST=cursor (manual override)
89
- // Escape hatch: EVOLVER_HOOK_VERBOSE=1 forces the message on regardless.
90
- function isCursorHost() {
91
- const verbose = String(process.env.EVOLVER_HOOK_VERBOSE || '').toLowerCase();
92
- if (verbose === '1' || verbose === 'true') return false;
93
- if (String(process.env.EVOLVER_HOOK_HOST || '').toLowerCase() === 'cursor') return true;
94
- if (String(process.env.TERM_PROGRAM || '').toLowerCase() === 'cursor') return true;
95
- if (process.env.CURSOR_TRACE_ID) return true;
96
- if (process.env.CURSOR_SESSION_ID) return true;
97
- return false;
98
- }
99
-
100
- function detectSignals(text) {
101
- if (!text) return [];
102
- const lower = text.toLowerCase();
103
- const signals = [];
104
- if (/error:|exception:|failed/i.test(lower)) signals.push('log_error');
105
- if (/timeout|slow|latency|bottleneck/i.test(lower)) signals.push('perf_bottleneck');
106
- if (/add|implement|feature|new function|new module/i.test(lower)) signals.push('user_feature_request');
107
- if (/improve|enhance|refactor|optimize/i.test(lower)) signals.push('user_improvement_suggestion');
108
- if (/not supported|unsupported|not implemented/i.test(lower)) signals.push('capability_gap');
109
- if (/deploy|ci|pipeline|build failed/i.test(lower)) signals.push('deployment_issue');
110
- if (/test fail|assertion|expect\(/i.test(lower)) signals.push('test_failure');
111
- return [...new Set(signals)];
112
- }
113
-
114
- function loadHubFetch() {
115
- const evolverRoot = findEvolverRoot();
116
- return require(path.join(evolverRoot, 'src', 'gep', 'hubFetch')).hubFetch;
117
- }
118
-
119
- async function recordToHub(outcome) {
120
- const hubUrl = process.env.EVOMAP_HUB_URL || process.env.A2A_HUB_URL;
121
- const apiKey = process.env.EVOMAP_API_KEY || process.env.A2A_NODE_SECRET;
122
- const nodeId = process.env.EVOMAP_NODE_ID || process.env.A2A_NODE_ID;
123
- if (!hubUrl || !apiKey) return false;
124
-
125
- try {
126
- const payload = JSON.stringify({
127
- gene_id: outcome.geneId || 'ad_hoc',
128
- signals: outcome.signals,
129
- status: outcome.status,
130
- score: outcome.score,
131
- summary: outcome.summary,
132
- sender_id: nodeId || undefined,
133
- });
134
-
135
- const endpoint = hubUrl.replace(/\/+$/, '') + '/a2a/evolution/record';
136
- const hubFetch = loadHubFetch();
137
- const res = await hubFetch(endpoint, {
138
- method: 'POST',
139
- headers: {
140
- 'Content-Type': 'application/json',
141
- 'Authorization': `Bearer ${apiKey}`,
142
- },
143
- body: payload,
144
- signal: AbortSignal.timeout(8000),
145
- });
146
- try { await res.text(); } catch (_) {}
147
- return res.ok;
148
- } catch {
149
- return false;
150
- }
151
- }
152
-
153
- function recordToLocal(graphPath, outcome) {
154
- try {
155
- // Resolve the project dir once so the cwd tag and the workspace_id secret
156
- // share a single, consistent source (both must agree with the session-start
157
- // reader's resolveProjectDir()-based scoping).
158
- const projectDir = resolveProjectDir();
159
- const entry = {
160
- timestamp: new Date().toISOString(),
161
- gene_id: outcome.geneId || 'ad_hoc',
162
- signals: outcome.signals,
163
- outcome: {
164
- status: outcome.status,
165
- score: outcome.score,
166
- note: outcome.summary,
167
- },
168
- // Tag the originating workspace so the review-time reader in
169
- // collect.js can scope user-level fallback entries to the current
170
- // cwd. Without this, two unrelated projects sharing the user-level
171
- // fallback file (~/.evolver/memory/evolution/memory_graph.jsonl,
172
- // used by npm-global installs) would cross-pollinate each other's
173
- // review context — a prompt-injection / disclosure surface flagged
174
- // by Bugbot on PR #105 round-2.
175
- //
176
- // workspace_id is the forge-resistant tag (PR #108 round-3): the
177
- // reader compares it against the secret in the workspace's own
178
- // .evolver/workspace-id file. cwd is retained as a backward-compat
179
- // tag so older entries written before this hardening still pass
180
- // the cwd check.
181
- //
182
- // Use resolveProjectDir() (NOT process.cwd()) so the cwd tag records the
183
- // user's project, consistent with how the diff above is collected and
184
- // with the session-start reader's cwd fallback. Under Cursor, cwd is the
185
- // plugin install dir, so a raw process.cwd() tag would never match the
186
- // reader's resolveProjectDir()-derived currentDir — silently hiding every
187
- // cwd-only entry (Bugbot PR #555). collect.js only uses cwd as a legacy
188
- // fallback (disabled once a workspace_id secret exists), so changing the
189
- // tag's source — still a directory path — does not affect its scoping.
190
- cwd: projectDir,
191
- workspace_id: resolveWorkspaceId(undefined, projectDir),
192
- source: 'hook:session-end',
193
- };
194
- fs.appendFileSync(graphPath, JSON.stringify(entry) + '\n', 'utf8');
195
- return true;
196
- } catch {
197
- return false;
198
- }
199
- }
200
-
201
- // Append a single timestamped line to ~/.evolver/logs/evolution.log (or
202
- // EVOLVER_HOOK_LOG_DIR). Best-effort: a log-write failure must never break the
203
- // hook. Used both for recorded outcomes and for the "skipped, nothing to
204
- // record" notices so a user can always see why a session did or did not
205
- // produce an entry.
206
- function appendEvolutionLog(line) {
207
- try {
208
- const logDir = process.env.EVOLVER_HOOK_LOG_DIR
209
- || path.join(os.homedir(), '.evolver', 'logs');
210
- fs.mkdirSync(logDir, { recursive: true });
211
- fs.appendFileSync(
212
- path.join(logDir, 'evolution.log'),
213
- `${new Date().toISOString()} ${line}\n`,
214
- 'utf8'
215
- );
216
- } catch { /* best-effort, never break the hook on log write */ }
217
- }
218
-
219
- function main() {
220
- let inputData = '';
221
- let handled = false;
222
- let watchdog = null;
223
- const finish = (payload) => {
224
- if (handled) return;
225
- handled = true;
226
- if (watchdog) clearTimeout(watchdog);
227
- process.stdout.write(JSON.stringify(payload || {}));
228
- process.exit(0);
229
- };
230
- process.stdin.setEncoding('utf8');
231
- process.stdin.on('data', chunk => { inputData += chunk; });
232
- process.stdin.on('end', () => {
233
- if (handled) return;
234
- // recordToHub is async (uses the shared Hub fetch transport); wrap the rest of the
235
- // handler in an immediately-invoked async function so we can await it
236
- // while still honouring the watchdog timeout and the `handled` guard.
237
- (async () => {
238
- try {
239
- const diffInfo = getGitDiffStats();
240
-
241
- if (!diffInfo.hasChanges) {
242
- // No git diff means no signal source — session-end derives the
243
- // outcome (status/score/signals/summary) entirely from the diff, so
244
- // there is nothing meaningful to record. This is expected in a
245
- // non-git workspace or a repo with no changes this session. Rather
246
- // than fabricate an empty outcome (which would pollute the memory
247
- // graph), record nothing — but leave a log breadcrumb so the user
248
- // can tell "evolver ran but had nothing to record" apart from
249
- // "evolver never fired".
250
- const reason = diffInfo.isRepo
251
- ? 'no changes detected this session'
252
- : 'not a git workspace';
253
- appendEvolutionLog(`[Evolution] Session end: nothing recorded (${reason}).`);
254
- finish({});
255
- return;
256
- }
257
-
258
- const signals = detectSignals(diffInfo.diffSnippet);
259
- if (signals.length === 0) signals.push('stable_success_plateau');
260
-
261
- const hasErrors = signals.includes('log_error') || signals.includes('test_failure');
262
- const status = hasErrors ? 'failed' : 'success';
263
- const score = hasErrors ? 0.3 : 0.8;
264
-
265
- const outcome = {
266
- geneId: 'ad_hoc',
267
- signals,
268
- status,
269
- score,
270
- summary: `Session end: ${diffInfo.summary}. Signals: [${signals.join(', ')}]`,
271
- };
272
-
273
- const evolverRoot = findEvolverRoot();
274
- const graphPath = findMemoryGraph(evolverRoot);
275
-
276
- // Local first: recordToHub is async with an 8s socket timeout, but
277
- // the 7s watchdog (setTimeout below) will process.exit(0) before a
278
- // slow hub returns — so anything sequenced after `await recordToHub`
279
- // can be silently skipped. recordToLocal is the reliable offline
280
- // fallback and must run regardless of hub latency.
281
- const localOk = graphPath ? recordToLocal(graphPath, outcome) : false;
282
- const hubOk = await recordToHub(outcome);
283
-
284
- const target = hubOk ? 'Hub' : localOk ? 'local memory' : 'nowhere (no Hub or local path)';
285
- const msg = `[Evolution] Session outcome recorded to ${target}: ${outcome.summary}`;
286
-
287
- // Stop hook output schema (per Claude Code docs):
288
- // - decision: "approve" | "block"
289
- // - reason: string (shown when decision is set)
290
- // - systemMessage: string (notification displayed to user)
291
- // - continue: boolean
292
- // - stopReason: string
293
- //
294
- // Earlier versions emitted `followup_message`, `stopMessage`, and
295
- // `additionalContext` together. `followup_message` is the field that
296
- // re-injects the receipt into Claude's next inference round, which
297
- // caused the agent to "respond" to its own evolution log line —
298
- // visible to users as an unexplained extra reasoning turn after
299
- // every task. The evolver is supposed to be observational, so we
300
- // now use `systemMessage` only — that surfaces the receipt to the
301
- // user without forcing another inference round.
302
- //
303
- // Cursor compatibility: Cursor's Claude Code-compatible runtime
304
- // currently treats `systemMessage` as a user prompt for the next
305
- // inference round. When we detect Cursor, omit systemMessage too.
306
- // The receipt is always appended to ~/.evolver/logs/evolution.log
307
- // so it is never silently lost; users can opt back in to the inline
308
- // notification with EVOLVER_HOOK_VERBOSE=1.
309
- appendEvolutionLog(msg);
310
-
311
- finish(isCursorHost() ? {} : { systemMessage: msg });
312
- } catch (e) {
313
- finish({});
314
- }
315
- })();
316
- });
317
-
318
- watchdog = setTimeout(() => finish({}), 7000);
319
- }
320
-
321
- main();