@evomap/evolver 1.89.2 → 1.89.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/.cursor/BUGBOT.md +182 -0
  2. package/.env.example +68 -0
  3. package/.git-commit-guard-token +1 -0
  4. package/.github/CODEOWNERS +63 -0
  5. package/.github/ISSUE_TEMPLATE/good_first_issue.md +23 -0
  6. package/.github/pull_request_template.md +45 -0
  7. package/.github/workflows/test.yml +75 -0
  8. package/CHANGELOG.md +1237 -0
  9. package/README.ja-JP.md +1 -3
  10. package/README.ko-KR.md +1 -3
  11. package/README.md +86 -530
  12. package/README.public.md +569 -0
  13. package/README.zh-CN.md +1 -3
  14. package/SECURITY.md +108 -0
  15. package/assets/gep/events.jsonl +3 -0
  16. package/assets/gep/genes.json +496 -0
  17. package/examples/atp-consumer-quickstart.md +100 -0
  18. package/examples/hello-world.md +38 -0
  19. package/index.js +44 -48
  20. package/package.json +6 -17
  21. package/proxy-package.json +39 -0
  22. package/public.manifest.json +143 -0
  23. package/src/adapters/hookAdapter.js +2 -0
  24. package/src/adapters/scripts/_lockPaths.js +74 -0
  25. package/src/adapters/scripts/evolver-session-start.js +19 -27
  26. package/src/config.js +23 -0
  27. package/src/evolve/guards.js +721 -1
  28. package/src/evolve/pipeline/collect.js +1283 -1
  29. package/src/evolve/pipeline/dispatch.js +421 -1
  30. package/src/evolve/pipeline/enrich.js +440 -1
  31. package/src/evolve/pipeline/hub.js +319 -1
  32. package/src/evolve/pipeline/select.js +274 -1
  33. package/src/evolve/pipeline/signals.js +206 -1
  34. package/src/evolve/utils.js +264 -1
  35. package/src/evolve.js +350 -1
  36. package/src/experiment/agentRunner.js +229 -0
  37. package/src/experiment/cli.js +159 -0
  38. package/src/experiment/comparison.js +233 -0
  39. package/src/experiment/metrics.js +75 -0
  40. package/src/forceUpdate.js +311 -30
  41. package/src/gep/a2aProtocol.js +4455 -1
  42. package/src/gep/antiAbuseTelemetry.js +233 -0
  43. package/src/gep/autoDistillConv.js +205 -1
  44. package/src/gep/autoDistillLlm.js +315 -1
  45. package/src/gep/candidateEval.js +92 -1
  46. package/src/gep/candidates.js +198 -1
  47. package/src/gep/contentHash.js +30 -1
  48. package/src/gep/conversationSniffer.js +266 -1
  49. package/src/gep/crypto.js +89 -1
  50. package/src/gep/curriculum.js +163 -1
  51. package/src/gep/deviceId.js +218 -1
  52. package/src/gep/envFingerprint.js +118 -1
  53. package/src/gep/epigenetics.js +31 -1
  54. package/src/gep/execBridge.js +711 -1
  55. package/src/gep/explore.js +289 -1
  56. package/src/gep/hash.js +15 -1
  57. package/src/gep/hubFetch.js +359 -1
  58. package/src/gep/hubReview.js +207 -1
  59. package/src/gep/hubSearch.js +526 -1
  60. package/src/gep/hubVerify.js +306 -1
  61. package/src/gep/learningSignals.js +89 -1
  62. package/src/gep/memoryGraph.js +1374 -1
  63. package/src/gep/memoryGraphAdapter.js +203 -1
  64. package/src/gep/mutation.js +203 -1
  65. package/src/gep/narrativeMemory.js +108 -1
  66. package/src/gep/openPRRegistry.js +205 -1
  67. package/src/gep/personality.js +423 -1
  68. package/src/gep/policyCheck.js +599 -1
  69. package/src/gep/prompt.js +836 -1
  70. package/src/gep/recallInject.js +409 -1
  71. package/src/gep/recallVerifier.js +318 -1
  72. package/src/gep/reflection.js +177 -1
  73. package/src/gep/sanitize.js +9 -0
  74. package/src/gep/selector.js +602 -1
  75. package/src/gep/skillDistiller.js +1294 -1
  76. package/src/gep/solidify.js +1699 -1
  77. package/src/gep/strategy.js +136 -1
  78. package/src/gep/tokenSavings.js +88 -1
  79. package/src/gep/validator/sandboxExecutor.js +29 -1
  80. package/src/gep/workspaceKeychain.js +174 -1
  81. package/src/proxy/extensions/traceControl.js +99 -1
  82. package/src/proxy/index.js +14 -5
  83. package/src/proxy/inject.js +52 -1
  84. package/src/proxy/lifecycle/manager.js +30 -0
  85. package/src/proxy/mailbox/store.js +2 -1
  86. package/src/proxy/router/messages_route.js +13 -2
  87. package/src/proxy/trace/extractor.js +646 -1
  88. package/src/proxy/trace/usage.js +105 -1
  89. package/CONTRIBUTING.md +0 -19
  90. package/assets/cover.png +0 -0
  91. package/assets/gep/genes.seed.json +0 -245
  92. package/scripts/a2a_export.js +0 -63
  93. package/scripts/a2a_ingest.js +0 -79
  94. package/scripts/a2a_promote.js +0 -118
  95. package/scripts/analyze_by_skill.js +0 -121
  96. package/scripts/build_binaries.js +0 -479
  97. package/scripts/check-changelog.js +0 -166
  98. package/scripts/extract_log.js +0 -85
  99. package/scripts/generate_history.js +0 -75
  100. package/scripts/gep_append_event.js +0 -96
  101. package/scripts/gep_personality_report.js +0 -234
  102. package/scripts/human_report.js +0 -147
  103. package/scripts/recall-verify-report.js +0 -234
  104. package/scripts/recover_loop.js +0 -61
  105. package/scripts/seed-merchants.js +0 -91
  106. package/scripts/suggest_version.js +0 -89
  107. package/scripts/validate-modules.js +0 -38
  108. package/scripts/validate-suite.js +0 -78
  109. package/skills/index.json +0 -14
  110. /package/{skills → bundled-skills}/_meta/SKILL.md +0 -0
@@ -0,0 +1,233 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('crypto');
4
+ const fs = require('fs');
5
+ const os = require('os');
6
+ const path = require('path');
7
+
8
+ const { captureEnvFingerprint, envFingerprintKey } = require('./envFingerprint');
9
+
10
+ const SCHEMA_VERSION = 'anti_abuse.v1';
11
+ const REDACTION_VERSION = 'anti_abuse_redaction.v1';
12
+ const DEFAULT_TTL_DAYS = 90;
13
+ const MAX_HASH_FILE_BYTES = 10 * 1024 * 1024;
14
+
15
+ function nowIso(now) {
16
+ if (now instanceof Date) return now.toISOString();
17
+ if (typeof now === 'string' && now) return now;
18
+ return new Date().toISOString();
19
+ }
20
+
21
+ function boolFromEnv(value) {
22
+ const v = String(value || '').trim().toLowerCase();
23
+ return v === '1' || v === 'true' || v === 'yes' || v === 'on';
24
+ }
25
+
26
+ function hmacPseudonym(value, opts) {
27
+ const raw = value == null ? '' : String(value);
28
+ const salt = opts && opts.salt ? String(opts.salt) : '';
29
+ const purpose = opts && opts.purpose ? String(opts.purpose) : 'anti_abuse';
30
+ if (!raw || !salt) return null;
31
+ return crypto.createHmac('sha256', salt).update(purpose).update('\0').update(raw).digest('hex').slice(0, 32);
32
+ }
33
+
34
+ function sha256File(filePath) {
35
+ let stat;
36
+ try {
37
+ stat = fs.statSync(filePath);
38
+ } catch (_) {
39
+ return null;
40
+ }
41
+ if (!stat.isFile() || stat.size > MAX_HASH_FILE_BYTES) return null;
42
+ const hash = crypto.createHash('sha256');
43
+ hash.update(fs.readFileSync(filePath));
44
+ return hash.digest('hex');
45
+ }
46
+
47
+ function safeJsonFile(filePath) {
48
+ try {
49
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
50
+ } catch (_) {
51
+ return null;
52
+ }
53
+ }
54
+
55
+ function filePermissionClass(filePath) {
56
+ let stat;
57
+ try {
58
+ stat = fs.statSync(filePath);
59
+ } catch (_) {
60
+ return 'missing';
61
+ }
62
+ if (!stat.isFile()) return 'not_file';
63
+ const mode = stat.mode & 0o777;
64
+ if ((mode & 0o077) === 0) return 'owner_only';
65
+ if ((mode & 0o007) !== 0) return 'world_accessible';
66
+ return 'group_accessible';
67
+ }
68
+
69
+ // The integrity envelope fingerprints the INSTALLED @evomap/evolver package
70
+ // — its package.json, CLI entry, and lockfiles — not the user's project.
71
+ // __dirname tracks this file inside the install (<pkg>/src/gep/), so two
72
+ // levels up is the package root in every install mode (dev clone, npm
73
+ // global, npx cache). Hashing getRepoRoot() (the user's project) here would
74
+ // make hub-side integrity checks compare workspace files against
75
+ // evolver_version, mismatching on every npm/global install.
76
+ function getEvolverPackageRoot() {
77
+ return path.resolve(__dirname, '..', '..');
78
+ }
79
+
80
+ // Containment gate for EVERY file the integrity envelope reads: resolve the
81
+ // candidate through symlinks and require it to stay inside the package root.
82
+ // Default-on heartbeat collection must not grow an out-of-tree file-read
83
+ // side effect — not via a tampered bin.evolver, and not via a symlinked
84
+ // package.json / lockfile either (each read is up to MAX_HASH_FILE_BYTES).
85
+ // Returns the real path when contained, else null (missing files land here
86
+ // too, matching sha256File's old null-on-ENOENT behavior).
87
+ function containedRealPath(root, candidate) {
88
+ try {
89
+ const realRoot = fs.realpathSync(root);
90
+ const real = fs.realpathSync(candidate);
91
+ return real.startsWith(realRoot + path.sep) ? real : null;
92
+ } catch (_) {
93
+ return null;
94
+ }
95
+ }
96
+
97
+ function collectIntegrityHashes(packageRoot) {
98
+ const root = packageRoot || getEvolverPackageRoot();
99
+ const pkgPath = containedRealPath(root, path.join(root, 'package.json'));
100
+ const pkg = (pkgPath && safeJsonFile(pkgPath)) || {};
101
+ const bin = pkg && pkg.bin && typeof pkg.bin.evolver === 'string' ? pkg.bin.evolver : null;
102
+ const binPath = bin ? containedRealPath(root, path.resolve(root, bin)) : null;
103
+ const lockfiles = ['package-lock.json', 'pnpm-lock.yaml', 'yarn.lock', 'bun.lockb'];
104
+ const lockfile_hashes = {};
105
+ for (const name of lockfiles) {
106
+ const p = containedRealPath(root, path.join(root, name));
107
+ const digest = p ? sha256File(p) : null;
108
+ if (digest) lockfile_hashes[name] = digest;
109
+ }
110
+ return {
111
+ package_json_hash: pkgPath ? sha256File(pkgPath) : null,
112
+ cli_entry_hash: binPath ? sha256File(binPath) : null,
113
+ lockfile_hashes,
114
+ };
115
+ }
116
+
117
+ function settingsPermissionClass(env) {
118
+ const e = env || process.env;
119
+ const settingsDir = e.EVOLVER_SETTINGS_DIR || path.join(os.homedir(), '.evolver');
120
+ return filePermissionClass(path.join(settingsDir, 'settings.json'));
121
+ }
122
+
123
+ function normalizeTaskMetrics(taskMeta) {
124
+ const meta = taskMeta && typeof taskMeta === 'object' ? taskMeta : {};
125
+ const metrics = meta.task_metrics && typeof meta.task_metrics === 'object' ? meta.task_metrics : null;
126
+ if (!metrics) return null;
127
+ return {
128
+ pending: Number.isFinite(Number(metrics.pending)) ? Number(metrics.pending) : null,
129
+ claimed: Number.isFinite(Number(metrics.claimed)) ? Number(metrics.claimed) : null,
130
+ completed: Number.isFinite(Number(metrics.completed)) ? Number(metrics.completed) : null,
131
+ failed: Number.isFinite(Number(metrics.failed)) ? Number(metrics.failed) : null,
132
+ avg_completion_ms: Number.isFinite(Number(metrics.avg_completion_ms)) ? Number(metrics.avg_completion_ms) : null,
133
+ };
134
+ }
135
+
136
+ function ttlDays(env) {
137
+ const raw = Number(env && env.EVOLVER_ANTI_ABUSE_TTL_DAYS);
138
+ return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : DEFAULT_TTL_DAYS;
139
+ }
140
+
141
+ function unavailable(field, reason, expectedSource) {
142
+ return {
143
+ field,
144
+ reason,
145
+ expected_source: expectedSource || 'hub_server',
146
+ };
147
+ }
148
+
149
+ function buildHeartbeatAntiAbuseTelemetry(opts) {
150
+ const options = opts || {};
151
+ const env = options.env || process.env;
152
+ const fp = options.envFingerprint || captureEnvFingerprint();
153
+ const salt = options.salt != null ? options.salt : env.EVOLVER_ANTI_ABUSE_SALT;
154
+ const saltId = options.saltId || env.EVOLVER_ANTI_ABUSE_SALT_ID || (salt ? 'env' : null);
155
+ const packageRoot = options.packageRoot || getEvolverPackageRoot();
156
+ const devicePseudonym = hmacPseudonym(fp && fp.device_id, { salt, purpose: 'device' });
157
+ const workspacePseudonym = hmacPseudonym(process.cwd(), { salt, purpose: 'workspace' });
158
+ const missing = [];
159
+ if (!devicePseudonym) missing.push(unavailable('device_pseudonym', 'anti_abuse_salt_missing', 'signed_policy_or_env'));
160
+ if (!workspacePseudonym) missing.push(unavailable('workspace_pseudonym', 'anti_abuse_salt_missing', 'signed_policy_or_env'));
161
+ missing.push(
162
+ unavailable('client_ip', 'server_observed_required', 'hub_edge'),
163
+ unavailable('asn', 'server_observed_required', 'hub_edge'),
164
+ unavailable('proxy_vpn_tor_datacenter_class', 'server_observed_required', 'hub_edge'),
165
+ unavailable('account_security', 'account_service_required', 'hub_account'),
166
+ unavailable('payout_method_token', 'payments_service_required', 'hub_payments'),
167
+ unavailable('risk_action_case', 'risk_engine_required', 'hub_risk')
168
+ );
169
+
170
+ return {
171
+ schema_version: SCHEMA_VERSION,
172
+ event_type: 'node.heartbeat',
173
+ purpose: 'anti_abuse',
174
+ pii_class: 'medium',
175
+ consent_level: 'default',
176
+ retention_ttl_days: ttlDays(env),
177
+ policy_version: env.EVOLVER_ANTI_ABUSE_POLICY_VERSION || 'local-default',
178
+ redaction_version: REDACTION_VERSION,
179
+ source: options.source || 'evolver-client',
180
+ generated_at: nowIso(options.now),
181
+ source_confidence: {
182
+ node_identity: 'client_attested',
183
+ device_integrity: 'client_attested',
184
+ task_metrics: 'client_attested',
185
+ network_source: 'server_observed_required',
186
+ account_security: 'server_observed_required',
187
+ payout: 'server_observed_required',
188
+ risk_decision: 'server_observed_required',
189
+ },
190
+ identity: {
191
+ node_id: options.nodeId || null,
192
+ account_id: null,
193
+ org_id: null,
194
+ },
195
+ device: {
196
+ device_pseudonym: devicePseudonym,
197
+ workspace_pseudonym: workspacePseudonym,
198
+ pseudonym_salt_id: saltId,
199
+ env_fingerprint_key: envFingerprintKey(fp),
200
+ platform: fp.platform || null,
201
+ arch: fp.arch || null,
202
+ os_release: fp.os_release || null,
203
+ node_version: fp.node_version || null,
204
+ evolver_version: fp.evolver_version || null,
205
+ client: fp.client || null,
206
+ client_version: fp.client_version || null,
207
+ model: fp.model || null,
208
+ region: fp.region || null,
209
+ container: !!fp.container,
210
+ },
211
+ integrity: collectIntegrityHashes(packageRoot),
212
+ local_security_boundary: {
213
+ proxy_bind_address_class: 'loopback',
214
+ // Env sniffing is only a fallback for the client heartbeat: the proxy
215
+ // lifecycle heartbeat runs INSIDE the proxy process (which usually has
216
+ // neither env var set) and passes its ground truth explicitly.
217
+ proxy_port_configured: options.proxyPortConfigured != null
218
+ ? !!options.proxyPortConfigured
219
+ : (boolFromEnv(env.EVOMAP_PROXY) || !!env.EVOMAP_PROXY_PORT),
220
+ settings_permission_class: settingsPermissionClass(env),
221
+ },
222
+ task_timing: normalizeTaskMetrics(options.taskMeta),
223
+ unavailable_fields: missing,
224
+ };
225
+ }
226
+
227
+ module.exports = {
228
+ SCHEMA_VERSION,
229
+ REDACTION_VERSION,
230
+ buildHeartbeatAntiAbuseTelemetry,
231
+ collectIntegrityHashes,
232
+ hmacPseudonym,
233
+ };