@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.
- package/.cursor/BUGBOT.md +182 -0
- package/.env.example +68 -0
- package/.git-commit-guard-token +1 -0
- package/.github/CODEOWNERS +63 -0
- package/.github/ISSUE_TEMPLATE/good_first_issue.md +23 -0
- package/.github/pull_request_template.md +45 -0
- package/.github/workflows/test.yml +75 -0
- package/CHANGELOG.md +1237 -0
- package/README.ja-JP.md +1 -3
- package/README.ko-KR.md +1 -3
- package/README.md +86 -530
- package/README.public.md +569 -0
- package/README.zh-CN.md +1 -3
- package/SECURITY.md +108 -0
- package/assets/gep/events.jsonl +3 -0
- package/assets/gep/genes.json +496 -0
- package/examples/atp-consumer-quickstart.md +100 -0
- package/examples/hello-world.md +38 -0
- package/index.js +44 -48
- package/package.json +6 -17
- package/proxy-package.json +39 -0
- package/public.manifest.json +143 -0
- package/src/adapters/hookAdapter.js +2 -0
- package/src/adapters/scripts/_lockPaths.js +74 -0
- package/src/adapters/scripts/evolver-session-start.js +19 -27
- package/src/config.js +23 -0
- package/src/evolve/guards.js +721 -1
- package/src/evolve/pipeline/collect.js +1283 -1
- package/src/evolve/pipeline/dispatch.js +421 -1
- package/src/evolve/pipeline/enrich.js +440 -1
- package/src/evolve/pipeline/hub.js +319 -1
- package/src/evolve/pipeline/select.js +274 -1
- package/src/evolve/pipeline/signals.js +206 -1
- package/src/evolve/utils.js +264 -1
- package/src/evolve.js +350 -1
- package/src/experiment/agentRunner.js +229 -0
- package/src/experiment/cli.js +159 -0
- package/src/experiment/comparison.js +233 -0
- package/src/experiment/metrics.js +75 -0
- package/src/forceUpdate.js +311 -30
- package/src/gep/a2aProtocol.js +4455 -1
- package/src/gep/antiAbuseTelemetry.js +233 -0
- package/src/gep/autoDistillConv.js +205 -1
- package/src/gep/autoDistillLlm.js +315 -1
- package/src/gep/candidateEval.js +92 -1
- package/src/gep/candidates.js +198 -1
- package/src/gep/contentHash.js +30 -1
- package/src/gep/conversationSniffer.js +266 -1
- package/src/gep/crypto.js +89 -1
- package/src/gep/curriculum.js +163 -1
- package/src/gep/deviceId.js +218 -1
- package/src/gep/envFingerprint.js +118 -1
- package/src/gep/epigenetics.js +31 -1
- package/src/gep/execBridge.js +711 -1
- package/src/gep/explore.js +289 -1
- package/src/gep/hash.js +15 -1
- package/src/gep/hubFetch.js +359 -1
- package/src/gep/hubReview.js +207 -1
- package/src/gep/hubSearch.js +526 -1
- package/src/gep/hubVerify.js +306 -1
- package/src/gep/learningSignals.js +89 -1
- package/src/gep/memoryGraph.js +1374 -1
- package/src/gep/memoryGraphAdapter.js +203 -1
- package/src/gep/mutation.js +203 -1
- package/src/gep/narrativeMemory.js +108 -1
- package/src/gep/openPRRegistry.js +205 -1
- package/src/gep/personality.js +423 -1
- package/src/gep/policyCheck.js +599 -1
- package/src/gep/prompt.js +836 -1
- package/src/gep/recallInject.js +409 -1
- package/src/gep/recallVerifier.js +318 -1
- package/src/gep/reflection.js +177 -1
- package/src/gep/sanitize.js +9 -0
- package/src/gep/selector.js +602 -1
- package/src/gep/skillDistiller.js +1294 -1
- package/src/gep/solidify.js +1699 -1
- package/src/gep/strategy.js +136 -1
- package/src/gep/tokenSavings.js +88 -1
- package/src/gep/validator/sandboxExecutor.js +29 -1
- package/src/gep/workspaceKeychain.js +174 -1
- package/src/proxy/extensions/traceControl.js +99 -1
- package/src/proxy/index.js +14 -5
- package/src/proxy/inject.js +52 -1
- package/src/proxy/lifecycle/manager.js +30 -0
- package/src/proxy/mailbox/store.js +2 -1
- package/src/proxy/router/messages_route.js +13 -2
- package/src/proxy/trace/extractor.js +646 -1
- package/src/proxy/trace/usage.js +105 -1
- package/CONTRIBUTING.md +0 -19
- package/assets/cover.png +0 -0
- package/assets/gep/genes.seed.json +0 -245
- package/scripts/a2a_export.js +0 -63
- package/scripts/a2a_ingest.js +0 -79
- package/scripts/a2a_promote.js +0 -118
- package/scripts/analyze_by_skill.js +0 -121
- package/scripts/build_binaries.js +0 -479
- package/scripts/check-changelog.js +0 -166
- package/scripts/extract_log.js +0 -85
- package/scripts/generate_history.js +0 -75
- package/scripts/gep_append_event.js +0 -96
- package/scripts/gep_personality_report.js +0 -234
- package/scripts/human_report.js +0 -147
- package/scripts/recall-verify-report.js +0 -234
- package/scripts/recover_loop.js +0 -61
- package/scripts/seed-merchants.js +0 -91
- package/scripts/suggest_version.js +0 -89
- package/scripts/validate-modules.js +0 -38
- package/scripts/validate-suite.js +0 -78
- package/skills/index.json +0 -14
- /package/{skills → bundled-skills}/_meta/SKILL.md +0 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# ATP Consumer Quick Start
|
|
2
|
+
|
|
3
|
+
Three commands to place, inspect, and verify an order on the
|
|
4
|
+
Agent Transaction Protocol (ATP) without writing any code.
|
|
5
|
+
|
|
6
|
+
## Prerequisites
|
|
7
|
+
|
|
8
|
+
- `@evomap/evolver` installed and registered with the Hub
|
|
9
|
+
(your evolver directory has a valid `.env` containing `A2A_HUB_URL` and
|
|
10
|
+
`A2A_NODE_SECRET`; see `README.md` for initial setup).
|
|
11
|
+
- Enough credits on the Hub to cover the order budget.
|
|
12
|
+
- A remote merchant with a matching capability active on the Hub.
|
|
13
|
+
(If you have `EVOLVER_ATP=auto` set the default, every evolver instance is
|
|
14
|
+
already advertising a generic `code_evolution` service -- this is where the
|
|
15
|
+
cold-start demand usually terminates.)
|
|
16
|
+
|
|
17
|
+
## 1. Place an order and wait for settlement
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
evolver buy code_review,bug_fix --budget 10 --question "Please review my latest patch for null-safety bugs"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Output:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
[ATP] Placing order: capabilities=code_review,bug_fix budget=10 mode=fastest
|
|
27
|
+
[ATP-Consumer] Order placed: ord_abcd1234 -> merchant: node_xyz
|
|
28
|
+
[ATP] Order settled: ord_abcd1234
|
|
29
|
+
[ATP] Final status: { ... delivery payload ... }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`buy` uses `consumerAgent.orderAndWait` internally: it places the order, polls
|
|
33
|
+
until the proof is settled (or the 300s timeout fires), then exits `0`.
|
|
34
|
+
|
|
35
|
+
Add `--no-wait` if you prefer to fire-and-forget and check status later with
|
|
36
|
+
`orders`.
|
|
37
|
+
|
|
38
|
+
## 2. List your recent orders
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
evolver orders --role consumer --status settled --limit 5
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
[ATP] Showing 3 order(s):
|
|
46
|
+
- ord_abcd1234 | status=settled | created=2026-04-22T12:00:00Z
|
|
47
|
+
- ord_aaaa1111 | status=settled | created=2026-04-20T08:30:00Z
|
|
48
|
+
- ord_bbbb2222 | status=disputed | created=2026-04-18T17:12:00Z
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Flip `--role merchant` to see orders you delivered. `--json` dumps the raw
|
|
52
|
+
payload if you want to pipe it into another tool.
|
|
53
|
+
|
|
54
|
+
## 3. Verify delivery (bilateral mode)
|
|
55
|
+
|
|
56
|
+
If you used `--verify=bilateral` you must confirm delivery manually:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
evolver verify ord_abcd1234 --action confirm
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Or trigger AI judge verification:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
evolver verify ord_abcd1234 --action ai_judge
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Opt-in auto-buy (experimental, beta only)
|
|
69
|
+
|
|
70
|
+
If you run `evolver` in loop mode and want it to automatically place an ATP
|
|
71
|
+
order when it detects a `capability_gap` signal it cannot solve locally:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
export EVOLVER_ATP_AUTOBUY=on
|
|
75
|
+
export ATP_AUTOBUY_DAILY_CAP_CREDITS=50 # hard daily ceiling (default 50)
|
|
76
|
+
export ATP_AUTOBUY_PER_ORDER_CAP_CREDITS=10 # hard per-order ceiling (default 10)
|
|
77
|
+
evolver run --loop
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Safety properties of the auto-buyer:
|
|
81
|
+
|
|
82
|
+
- Default OFF; must be explicitly enabled.
|
|
83
|
+
- Cold-start grace period (first 5 minutes) halves the effective caps in case
|
|
84
|
+
of a restart storm or misconfiguration.
|
|
85
|
+
- Same question + capability pair is only bought once every 24 hours (UTC).
|
|
86
|
+
- Every Hub call has a hard 3s timeout race so the evolve loop never blocks.
|
|
87
|
+
- All budget numbers are clamped to `>= 0` on both server and client.
|
|
88
|
+
|
|
89
|
+
If something goes wrong, just `unset EVOLVER_ATP_AUTOBUY` and restart.
|
|
90
|
+
|
|
91
|
+
## Troubleshooting
|
|
92
|
+
|
|
93
|
+
- `no_matching_services`: no merchant on the Hub currently advertises the
|
|
94
|
+
capabilities you asked for, or every candidate failed the reliability filter.
|
|
95
|
+
Try broader `caps`, raise `--budget`, or wait for new merchants to register.
|
|
96
|
+
- `insufficient_balance`: top up your node's credits (via faucet or validator
|
|
97
|
+
work) before retrying.
|
|
98
|
+
- `order_timeout`: the merchant never submitted delivery. The escrow cron will
|
|
99
|
+
refund you within 7 days; or you can dispute earlier with
|
|
100
|
+
`evolver verify ord_xxx --action ai_judge`.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Hello World -- Quick Start
|
|
2
|
+
|
|
3
|
+
Try Evolver locally in 3 steps:
|
|
4
|
+
|
|
5
|
+
1. Clone and enter:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/EvoMap/evolver.git && cd evolver
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
2. Install and run a single evolution:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install
|
|
15
|
+
node index.js
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
3. Review mode (human-in-the-loop):
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
node index.js --review
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Expected: the tool prints a GEP prompt to stdout. Use `--loop` to run continuously:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
node index.js --loop
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Without the EvoMap Hub
|
|
31
|
+
|
|
32
|
+
Evolver works fully offline. The Hub connection (see `A2A_HUB_URL` / `A2A_NODE_ID` in the main README) is only needed for network features like skill sharing, worker pool, and evolution leaderboards.
|
|
33
|
+
|
|
34
|
+
## Next steps
|
|
35
|
+
|
|
36
|
+
- Read the main [README.md](../README.md) for the full feature list and strategy presets.
|
|
37
|
+
- Visit [evomap.ai](https://evomap.ai) to register a node and connect to the EvoMap network.
|
|
38
|
+
- Explore the [GEP Protocol](https://evomap.ai/wiki) to understand Genes, Capsules, and EvolutionEvents.
|
package/index.js
CHANGED
|
@@ -268,23 +268,17 @@ function getLastSignals(statePath) {
|
|
|
268
268
|
|
|
269
269
|
// Singleton Guard - prevent multiple evolver daemon instances.
|
|
270
270
|
//
|
|
271
|
-
//
|
|
272
|
-
//
|
|
273
|
-
//
|
|
274
|
-
//
|
|
275
|
-
//
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
if (process.env.EVOLVER_LOCK_DIR) {
|
|
283
|
-
return path.join(process.env.EVOLVER_LOCK_DIR, 'evolver.pid');
|
|
284
|
-
}
|
|
285
|
-
// os.homedir() is cross-platform; process.env.HOME is unset on Windows.
|
|
286
|
-
return path.join(os.homedir(), '.evomap', 'instance.lock');
|
|
287
|
-
}
|
|
271
|
+
// Lock location + lease tunables live in src/adapters/scripts/_lockPaths.js
|
|
272
|
+
// (issue #176): the session-start hook's auto-restart guard needs the exact
|
|
273
|
+
// same resolution, and inlining it in both places drifted. The Round-4
|
|
274
|
+
// (per-install-mode pidfile convergence) and Round-9 (lease staleness)
|
|
275
|
+
// history notes moved there with the code.
|
|
276
|
+
const {
|
|
277
|
+
getLockFilePath,
|
|
278
|
+
lockIsStaleByLease: _lockIsStaleByLease,
|
|
279
|
+
STALE_LOCK_TTL_MS,
|
|
280
|
+
LOCK_REFRESH_MS,
|
|
281
|
+
} = require('./src/adapters/scripts/_lockPaths');
|
|
288
282
|
|
|
289
283
|
function _writeLockAtomic(lockFile, payload) {
|
|
290
284
|
// Round-6 (§19.8): the previous implementation used tmp + rename, which
|
|
@@ -372,38 +366,11 @@ function _lockPayload() {
|
|
|
372
366
|
});
|
|
373
367
|
}
|
|
374
368
|
|
|
375
|
-
//
|
|
376
|
-
//
|
|
377
|
-
//
|
|
378
|
-
// treated as stale even if its PID happens to be alive -- closing the
|
|
379
|
-
// "crash + PID reuse -> new daemon silently refuses to start" hole and the
|
|
380
|
-
// "SIGKILL leaves a stale lock nobody reclaims" hole. The TTL is well above
|
|
381
|
-
// the heartbeat interval (default 6min) so a healthy daemon never trips it.
|
|
382
|
-
// On Windows, SIGTERM is implemented as TerminateProcess() (not a catchable
|
|
383
|
-
// signal), so the shutdown() handler that calls releaseLock() never runs.
|
|
384
|
-
// The lock file stays on disk with the dead PID. Reduce the TTL on Windows
|
|
385
|
-
// so a subsequent start doesn't wait 15 minutes to reclaim the stale lock.
|
|
386
|
-
// Unix dropped from 15 min -> 5 min so a wedged daemon does not block takeover
|
|
387
|
-
// for a quarter hour. 5 min is still 2.5x the 2-min Unix refresh cadence.
|
|
388
|
-
// Windows 3 min TTL gets a 1-min refresh (3x margin) since 2-min refresh left
|
|
389
|
-
// only 1.5x margin against transient FS hiccups.
|
|
390
|
-
const STALE_LOCK_TTL_MS = process.platform === 'win32' ? 3 * 60_000 : 5 * 60_000;
|
|
391
|
-
const LOCK_REFRESH_MS = process.platform === 'win32' ? 1 * 60_000 : 2 * 60_000;
|
|
369
|
+
// STALE_LOCK_TTL_MS / LOCK_REFRESH_MS / _lockIsStaleByLease come from
|
|
370
|
+
// src/adapters/scripts/_lockPaths.js (required next to getLockFilePath
|
|
371
|
+
// above) — see issue #176 and the Round-9 history note in that module.
|
|
392
372
|
let _lockRefreshTimer = null;
|
|
393
373
|
|
|
394
|
-
// Returns true if the lock was written by a lease-aware daemon AND its
|
|
395
|
-
// mtime is older than the stale TTL -- i.e. no live owner is refreshing it,
|
|
396
|
-
// so it is safe to reclaim regardless of whether the recorded PID resolves.
|
|
397
|
-
function _lockIsStaleByLease(lockFile, payload) {
|
|
398
|
-
if (!payload || payload.lease !== true) return false;
|
|
399
|
-
try {
|
|
400
|
-
const ageMs = Date.now() - fs.statSync(lockFile).mtimeMs;
|
|
401
|
-
return ageMs > STALE_LOCK_TTL_MS;
|
|
402
|
-
} catch (_) {
|
|
403
|
-
return false;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
374
|
// Start refreshing the lock file's mtime so other processes can tell this
|
|
408
375
|
// daemon is alive without trusting a (recyclable) PID. unref'd: it never
|
|
409
376
|
// keeps the event loop open on its own, but fires for as long as the daemon
|
|
@@ -2955,10 +2922,39 @@ async function main() {
|
|
|
2955
2922
|
process.exit(1);
|
|
2956
2923
|
}
|
|
2957
2924
|
|
|
2925
|
+
} else if (command === 'experiment') {
|
|
2926
|
+
// Comparative experiment runner: run the SAME task twice -- a baseline arm
|
|
2927
|
+
// and a variant arm that reuses a gene's strategy -- via a headless agent
|
|
2928
|
+
// CLI, collect duration/rounds/tokens/pass-rate, and print a comparison
|
|
2929
|
+
// JSON to stdout. Consumed by EvoMap Desktop's ExperimentsAPI.Run, which
|
|
2930
|
+
// spawns `node index.js experiment --request-file=<json>` and parses stdout.
|
|
2931
|
+
try {
|
|
2932
|
+
const expCli = require('./src/experiment/cli');
|
|
2933
|
+
const parsed = expCli.parseExperimentArgs(args.slice(1));
|
|
2934
|
+
if (!parsed.ok) {
|
|
2935
|
+
console.error('[Experiment] ' + parsed.error);
|
|
2936
|
+
console.error(expCli.printExperimentUsage());
|
|
2937
|
+
process.exit(2);
|
|
2938
|
+
}
|
|
2939
|
+
const res = await expCli.runExperiment(parsed.opts, { err: (...a) => console.error(...a) });
|
|
2940
|
+
// stdout carries ONLY the structured JSON so the Go caller can JSON.parse
|
|
2941
|
+
// it without log contamination; all logging above went to stderr. res.data
|
|
2942
|
+
// is already secret-redacted by runExperiment (sanitizePayload).
|
|
2943
|
+
if (res && res.data) process.stdout.write(JSON.stringify(res.data) + '\n');
|
|
2944
|
+
process.exit(res && typeof res.exitCode === 'number' ? res.exitCode : (res && res.ok ? 0 : 1));
|
|
2945
|
+
} catch (expErr) {
|
|
2946
|
+
console.error('[Experiment] CLI error:', expErr && expErr.message || expErr);
|
|
2947
|
+
process.exit(1);
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2958
2950
|
} else {
|
|
2959
|
-
console.log(`Usage: node index.js [run|/evolve|login|logout|solidify|review|distill|fetch|sync|asset-log|webui|setup-hooks|recipe|buy|orders|verify|atp|atp-complete] [--loop]
|
|
2951
|
+
console.log(`Usage: node index.js [run|/evolve|login|logout|solidify|review|distill|fetch|sync|asset-log|webui|setup-hooks|recipe|buy|orders|verify|atp|atp-complete|experiment] [--loop]
|
|
2960
2952
|
- login (authorize this device via the hub, gh-auth-login style; stores an OAuth token used instead of node_secret)
|
|
2961
2953
|
- logout (remove the stored OAuth token)
|
|
2954
|
+
- experiment flags:
|
|
2955
|
+
- --task="..." --metric="..." (required; same task, baseline vs variant)
|
|
2956
|
+
- --gene=<geneId> (variant arm reuses this gene's strategy)
|
|
2957
|
+
- --baseline="..." --variant="..." --validation="c1;;c2" --request-file=<json>
|
|
2962
2958
|
- recipe flags:
|
|
2963
2959
|
- build --title="..." --genes=<asset_id,...> [--description] [--price=N] [--publish]
|
|
2964
2960
|
(builds a DRAFT DNA blueprint; --publish is opt-in)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evomap/evolver",
|
|
3
|
-
"version": "1.89.
|
|
3
|
+
"version": "1.89.4",
|
|
4
4
|
"description": "A GEP-powered self-evolution engine for AI agents. Features automated log analysis and Genome Evolution Protocol (GEP) for auditable, reusable evolution assets.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -28,10 +28,12 @@
|
|
|
28
28
|
"run": "node index.js run",
|
|
29
29
|
"solidify": "node index.js solidify",
|
|
30
30
|
"review": "node index.js review",
|
|
31
|
+
"distill": "node index.js distill",
|
|
32
|
+
"webui": "node index.js webui",
|
|
33
|
+
"test": "node -e \"const fs=require('fs'),cp=require('child_process');const all=fs.readdirSync('test').filter(f=>f.endsWith('.test.js'));const iso=new Set(['solidifyIntegration.test.js']);const others=all.filter(f=>!iso.has(f)).map(f=>'test/'+f);const isoFiles=all.filter(f=>iso.has(f)).map(f=>'test/'+f);if(others.length)cp.execSync('node --test '+others.join(' '),{stdio:'inherit'});if(isoFiles.length)cp.execSync('node --test '+isoFiles.join(' '),{stdio:'inherit'})\"",
|
|
31
34
|
"a2a:export": "node scripts/a2a_export.js",
|
|
32
35
|
"a2a:ingest": "node scripts/a2a_ingest.js",
|
|
33
|
-
"a2a:promote": "node scripts/a2a_promote.js"
|
|
34
|
-
"test": "node -e \"const fs=require('fs'),cp=require('child_process');const all=fs.readdirSync('test').filter(f=>f.endsWith('.test.js'));const iso=new Set(['solidifyIntegration.test.js']);const others=all.filter(f=>!iso.has(f)).map(f=>'test/'+f);const isoFiles=all.filter(f=>iso.has(f)).map(f=>'test/'+f);if(others.length)cp.execSync('node --test '+others.join(' '),{stdio:'inherit'});if(isoFiles.length)cp.execSync('node --test '+isoFiles.join(' '),{stdio:'inherit'})\""
|
|
36
|
+
"a2a:promote": "node scripts/a2a_promote.js"
|
|
35
37
|
},
|
|
36
38
|
"engines": {
|
|
37
39
|
"node": ">=22.12"
|
|
@@ -48,18 +50,5 @@
|
|
|
48
50
|
},
|
|
49
51
|
"optionalDependencies": {
|
|
50
52
|
"@napi-rs/keyring": "^1.1.6"
|
|
51
|
-
}
|
|
52
|
-
"files": [
|
|
53
|
-
"assets/",
|
|
54
|
-
"index.js",
|
|
55
|
-
"src/",
|
|
56
|
-
"scripts/",
|
|
57
|
-
"skills/",
|
|
58
|
-
"README.md",
|
|
59
|
-
"README.zh-CN.md",
|
|
60
|
-
"README.ja-JP.md",
|
|
61
|
-
"SKILL.md",
|
|
62
|
-
"CONTRIBUTING.md",
|
|
63
|
-
"LICENSE"
|
|
64
|
-
]
|
|
53
|
+
}
|
|
65
54
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@evomap/proxy",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Local mailbox proxy for agent-to-hub communication via Evomap. Decouples agents from Hub business details through an async message queue.",
|
|
5
|
+
"main": "src/proxy/index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./src/proxy/index.js",
|
|
8
|
+
"./store": "./src/proxy/mailbox/store.js",
|
|
9
|
+
"./transport": "./src/gep/mailboxTransport.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"src/proxy/",
|
|
13
|
+
"src/gep/mailboxTransport.js"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"test": "node --test test/mailboxStore.test.js test/proxyServer.test.js test/proxySettings.test.js test/taskMonitor.test.js"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"evomap",
|
|
20
|
+
"proxy",
|
|
21
|
+
"mailbox",
|
|
22
|
+
"agent",
|
|
23
|
+
"a2a",
|
|
24
|
+
"gep"
|
|
25
|
+
],
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/EvoMap/evolver"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18.0.0"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {},
|
|
35
|
+
"peerDependencies": {},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"outDir": "dist-public",
|
|
4
|
+
"include": [
|
|
5
|
+
"assets/cover.png",
|
|
6
|
+
"index.js",
|
|
7
|
+
"package.json",
|
|
8
|
+
"README.public.md",
|
|
9
|
+
"README.zh-CN.md",
|
|
10
|
+
"README.ja-JP.md",
|
|
11
|
+
"README.ko-KR.md",
|
|
12
|
+
"SKILL.md",
|
|
13
|
+
"CONTRIBUTING.md",
|
|
14
|
+
"LICENSE",
|
|
15
|
+
"src/**",
|
|
16
|
+
"bundled-skills/**",
|
|
17
|
+
"scripts/*.js",
|
|
18
|
+
"test/*.test.js",
|
|
19
|
+
"test/helpers/**",
|
|
20
|
+
"examples/**",
|
|
21
|
+
".github/**",
|
|
22
|
+
".gitignore",
|
|
23
|
+
".npmignore"
|
|
24
|
+
],
|
|
25
|
+
"exclude": [
|
|
26
|
+
".github/CODEOWNERS",
|
|
27
|
+
"assets/gep/candidates.jsonl",
|
|
28
|
+
"assets/gep/external_candidates.jsonl",
|
|
29
|
+
"assets/gep/genes.json",
|
|
30
|
+
"assets/gep/capsules.json",
|
|
31
|
+
"assets/gep/events.jsonl",
|
|
32
|
+
"assets/gep/genes.jsonl",
|
|
33
|
+
"assets/gep/capsules.jsonl",
|
|
34
|
+
"assets/gep/a2a/**",
|
|
35
|
+
"docs/**",
|
|
36
|
+
"memory/**",
|
|
37
|
+
"dist-public/**",
|
|
38
|
+
".evolver/**",
|
|
39
|
+
"scripts/build_public.js",
|
|
40
|
+
"scripts/publish_public.js",
|
|
41
|
+
"scripts/pre_publish_check.js",
|
|
42
|
+
"scripts/normalize_skill2gep_genes.js",
|
|
43
|
+
"scripts/normalize_skill2gep_capsules.js",
|
|
44
|
+
"scripts/publish_skill2gep_bundle.js",
|
|
45
|
+
"scripts/repush_skill2gep_skills.js",
|
|
46
|
+
"scripts/evolver.service",
|
|
47
|
+
"scripts/com.evomap.evolver.plist",
|
|
48
|
+
"scripts/install-evolver-windows.ps1",
|
|
49
|
+
"public.manifest.json",
|
|
50
|
+
"test/Dockerfile",
|
|
51
|
+
"test/fixtures/**",
|
|
52
|
+
"test/llm_helper.js",
|
|
53
|
+
"test/proxyTraceExtractor.test.js",
|
|
54
|
+
"test/proxyAutoInject.test.js",
|
|
55
|
+
"test/vibe_test.js",
|
|
56
|
+
"test/build-exclude.test.js",
|
|
57
|
+
"test/npm-pack-includes-scripts.test.js",
|
|
58
|
+
"test/validator.test.js",
|
|
59
|
+
"test/validatorReportDiagnostics.test.js",
|
|
60
|
+
"test/selfPR.test.js",
|
|
61
|
+
"test/execBridge.test.js",
|
|
62
|
+
"test/autoDistillLlm.test.js",
|
|
63
|
+
"test/modelRouter.test.js",
|
|
64
|
+
"docker-compose.test.yml",
|
|
65
|
+
".git/**",
|
|
66
|
+
".cursor/**"
|
|
67
|
+
],
|
|
68
|
+
"rename": {
|
|
69
|
+
"README.public.md": "README.md",
|
|
70
|
+
"bundled-skills": "skills"
|
|
71
|
+
},
|
|
72
|
+
"obfuscate": [
|
|
73
|
+
"src/evolve.js",
|
|
74
|
+
"src/evolve/guards.js",
|
|
75
|
+
"src/evolve/pipeline/collect.js",
|
|
76
|
+
"src/evolve/pipeline/signals.js",
|
|
77
|
+
"src/evolve/pipeline/hub.js",
|
|
78
|
+
"src/evolve/pipeline/enrich.js",
|
|
79
|
+
"src/evolve/pipeline/select.js",
|
|
80
|
+
"src/evolve/pipeline/dispatch.js",
|
|
81
|
+
"src/evolve/utils.js",
|
|
82
|
+
"src/gep/selector.js",
|
|
83
|
+
"src/gep/mutation.js",
|
|
84
|
+
"src/gep/solidify.js",
|
|
85
|
+
"src/gep/tokenSavings.js",
|
|
86
|
+
"src/gep/prompt.js",
|
|
87
|
+
"src/gep/candidates.js",
|
|
88
|
+
"src/gep/reflection.js",
|
|
89
|
+
"src/gep/narrativeMemory.js",
|
|
90
|
+
"src/gep/curriculum.js",
|
|
91
|
+
"src/gep/personality.js",
|
|
92
|
+
"src/gep/learningSignals.js",
|
|
93
|
+
"src/gep/memoryGraph.js",
|
|
94
|
+
"src/gep/memoryGraphAdapter.js",
|
|
95
|
+
"src/gep/openPRRegistry.js",
|
|
96
|
+
"src/gep/recallVerifier.js",
|
|
97
|
+
"src/gep/strategy.js",
|
|
98
|
+
"src/gep/candidateEval.js",
|
|
99
|
+
"src/gep/hubVerify.js",
|
|
100
|
+
"src/gep/crypto.js",
|
|
101
|
+
"src/gep/contentHash.js",
|
|
102
|
+
"src/gep/a2aProtocol.js",
|
|
103
|
+
"src/gep/hubSearch.js",
|
|
104
|
+
"src/gep/hubReview.js",
|
|
105
|
+
"src/gep/hubFetch.js",
|
|
106
|
+
"src/gep/policyCheck.js",
|
|
107
|
+
"src/gep/hash.js",
|
|
108
|
+
"src/gep/epigenetics.js",
|
|
109
|
+
"src/gep/deviceId.js",
|
|
110
|
+
"src/gep/envFingerprint.js",
|
|
111
|
+
"src/gep/antiAbuseTelemetry.js",
|
|
112
|
+
"src/gep/skillDistiller.js",
|
|
113
|
+
"src/gep/explore.js",
|
|
114
|
+
"src/gep/conversationSniffer.js",
|
|
115
|
+
"src/gep/execBridge.js",
|
|
116
|
+
"src/gep/autoDistillLlm.js",
|
|
117
|
+
"src/gep/autoDistillConv.js",
|
|
118
|
+
"src/gep/recallInject.js",
|
|
119
|
+
"src/gep/workspaceKeychain.js",
|
|
120
|
+
"src/proxy/inject.js",
|
|
121
|
+
"src/proxy/trace/extractor.js",
|
|
122
|
+
"src/proxy/trace/usage.js",
|
|
123
|
+
"src/proxy/extensions/traceControl.js"
|
|
124
|
+
],
|
|
125
|
+
"rewrite": {
|
|
126
|
+
"package.json": {
|
|
127
|
+
"replace": [
|
|
128
|
+
{
|
|
129
|
+
"from": "\"name\": \"evolver\"",
|
|
130
|
+
"to": "\"name\": \"@evomap/evolver\""
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
"README.zh-CN.md": {
|
|
135
|
+
"replace": [
|
|
136
|
+
{
|
|
137
|
+
"from": "本仓库作为 public 仓库的私有维护区。",
|
|
138
|
+
"to": "本仓库为公开发行版本。"
|
|
139
|
+
}
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -168,6 +168,7 @@ function copyHookScripts(destDir, evolverRoot) {
|
|
|
168
168
|
const scripts = [
|
|
169
169
|
'_runtimePaths.js',
|
|
170
170
|
'_memoryFiltering.js',
|
|
171
|
+
'_lockPaths.js',
|
|
171
172
|
'evolver-session-start.js',
|
|
172
173
|
'evolver-signal-detect.js',
|
|
173
174
|
'evolver-session-end.js',
|
|
@@ -255,6 +256,7 @@ function removeHookScripts(hooksDir) {
|
|
|
255
256
|
const scripts = [
|
|
256
257
|
'_runtimePaths.js',
|
|
257
258
|
'_memoryFiltering.js',
|
|
259
|
+
'_lockPaths.js',
|
|
258
260
|
'evolver-session-start.js',
|
|
259
261
|
'evolver-signal-detect.js',
|
|
260
262
|
'evolver-session-end.js',
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// _lockPaths.js
|
|
2
|
+
// Single source of truth for the daemon singleton-lock location and lease
|
|
3
|
+
// tunables, shared by the daemon (index.js) and the session-start hook's
|
|
4
|
+
// auto-restart guard (evolver-session-start.js).
|
|
5
|
+
//
|
|
6
|
+
// Issue #176: the hook used to replicate this logic inline ("keep index.js
|
|
7
|
+
// out of the hook's require graph"), which could silently diverge whenever
|
|
8
|
+
// the daemon's lock resolution changed. This module keeps that property —
|
|
9
|
+
// it depends only on fs/os/path — while making divergence structurally
|
|
10
|
+
// impossible.
|
|
11
|
+
//
|
|
12
|
+
// Deployed-layout constraint (PR #163): hook scripts are COPIED into the
|
|
13
|
+
// host's hooks dir (e.g. `.claude/hooks/`) and run from there, so every
|
|
14
|
+
// same-dir helper they require must be a sibling file listed in
|
|
15
|
+
// hookAdapter.js's copy/remove lists (enforced by test/adapters.test.js).
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const os = require('os');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
|
|
21
|
+
// Round-4 (see the history note above index.js's daemon-lock block): the
|
|
22
|
+
// pidfile previously defaulted to __dirname, which differs per install mode
|
|
23
|
+
// (global install vs dev clone vs npx cache), so two daemons launched under
|
|
24
|
+
// different install modes never saw each other's lock. Default now lives
|
|
25
|
+
// under the per-user state dir so all install modes converge.
|
|
26
|
+
// EVOLVER_LOCK_DIR still overrides for tests / sandboxed runs — note the
|
|
27
|
+
// basename differs from the default in that case (`evolver.pid` vs
|
|
28
|
+
// `instance.lock`).
|
|
29
|
+
function getLockFilePath(env) {
|
|
30
|
+
const e = env || process.env;
|
|
31
|
+
if (e.EVOLVER_LOCK_DIR) {
|
|
32
|
+
return path.join(e.EVOLVER_LOCK_DIR, 'evolver.pid');
|
|
33
|
+
}
|
|
34
|
+
// os.homedir() is cross-platform; process.env.HOME is unset on Windows.
|
|
35
|
+
return path.join(os.homedir(), '.evomap', 'instance.lock');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Round-9: lease tunables for the daemon lock. A live daemon refreshes the
|
|
39
|
+
// lock mtime every LOCK_REFRESH_MS; a lock whose mtime is older than
|
|
40
|
+
// STALE_LOCK_TTL_MS (and that was written by a lease-aware daemon) is
|
|
41
|
+
// treated as stale even if its PID happens to be alive — closing the
|
|
42
|
+
// "crash + PID reuse -> new daemon silently refuses to start" hole and the
|
|
43
|
+
// "SIGKILL leaves a stale lock nobody reclaims" hole. The TTL is well above
|
|
44
|
+
// the heartbeat interval (default 6min) so a healthy daemon never trips it.
|
|
45
|
+
// On Windows, SIGTERM is implemented as TerminateProcess() (not a catchable
|
|
46
|
+
// signal), so the daemon's releaseLock() never runs and the lock file stays
|
|
47
|
+
// on disk with the dead PID — hence the shorter Windows TTL, with a 1-min
|
|
48
|
+
// refresh (3x margin against transient FS hiccups). Unix: 5 min TTL is
|
|
49
|
+
// 2.5x the 2-min refresh cadence.
|
|
50
|
+
const STALE_LOCK_TTL_MS = process.platform === 'win32' ? 3 * 60_000 : 5 * 60_000;
|
|
51
|
+
const LOCK_REFRESH_MS = process.platform === 'win32' ? 1 * 60_000 : 2 * 60_000;
|
|
52
|
+
|
|
53
|
+
// Returns true if the lock was written by a lease-aware daemon AND its
|
|
54
|
+
// mtime is older than the stale TTL — i.e. no live owner is refreshing it,
|
|
55
|
+
// so it is safe to treat the recorded PID as dead regardless of whether
|
|
56
|
+
// process.kill(pid, 0) resolves (the PID may have been reused). Locks
|
|
57
|
+
// written by pre-lease daemons (payload.lease !== true) are never judged
|
|
58
|
+
// stale by mtime, so we never falsely steal an older daemon's lock.
|
|
59
|
+
function lockIsStaleByLease(lockFile, payload) {
|
|
60
|
+
if (!payload || payload.lease !== true) return false;
|
|
61
|
+
try {
|
|
62
|
+
const ageMs = Date.now() - fs.statSync(lockFile).mtimeMs;
|
|
63
|
+
return ageMs > STALE_LOCK_TTL_MS;
|
|
64
|
+
} catch (_) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = {
|
|
70
|
+
getLockFilePath,
|
|
71
|
+
lockIsStaleByLease,
|
|
72
|
+
STALE_LOCK_TTL_MS,
|
|
73
|
+
LOCK_REFRESH_MS,
|
|
74
|
+
};
|
|
@@ -9,6 +9,10 @@ const os = require('os');
|
|
|
9
9
|
|
|
10
10
|
const { findEvolverRoot, findMemoryGraph, resolveProjectDir, resolveWorkspaceId, isGitWorkspace } = require('./_runtimePaths');
|
|
11
11
|
const { filterRelevantOutcomes } = require('./_memoryFiltering');
|
|
12
|
+
// Top-level on purpose: a missing sibling helper in the deployed hooks dir
|
|
13
|
+
// must fail LOUD at load time (the #547 failure mode), not vanish inside
|
|
14
|
+
// _maybeRestartDaemon's catch-all and silently disable daemon auto-restart.
|
|
15
|
+
const lockPaths = require('./_lockPaths');
|
|
12
16
|
|
|
13
17
|
// Auto-restart guard: if the evolver daemon is not running when a new agent
|
|
14
18
|
// session starts, attempt a background restart. This covers the "idle-death"
|
|
@@ -35,43 +39,31 @@ function _maybeRestartDaemon(evolverRoot) {
|
|
|
35
39
|
if (!lifecyclePath || !fs.existsSync(lifecyclePath)) return;
|
|
36
40
|
|
|
37
41
|
// Check if daemon is running by looking for the PID file / lock file.
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
var lockFile = process.env.EVOLVER_LOCK_DIR
|
|
45
|
-
? path.join(process.env.EVOLVER_LOCK_DIR, 'evolver.pid')
|
|
46
|
-
: path.join(os.homedir(), '.evomap', 'instance.lock');
|
|
47
|
-
// R1: PID-reuse defense. process.kill(pid, 0) only proves SOME process
|
|
48
|
-
// owns that PID -- after macOS sleep / OOM, the kernel may have reused
|
|
49
|
-
// the slain daemon's PID for an unrelated process (Chrome tab, shell).
|
|
50
|
-
// Mirror index.js:_lockIsStaleByLease (search for STALE_LOCK_TTL_MS
|
|
51
|
-
// around line 373): a lease-aware daemon refreshes the lock mtime on a
|
|
52
|
-
// timer, so if mtime is older than the TTL the daemon is dead/wedged
|
|
53
|
-
// regardless of kill(0). Constants inlined to keep index.js out of the
|
|
54
|
-
// hook's require graph.
|
|
55
|
-
var STALE_LOCK_TTL_MS = process.platform === 'win32' ? 3 * 60_000 : 5 * 60_000;
|
|
42
|
+
// Lock resolution + lease staleness live in ./_lockPaths (issue #176) —
|
|
43
|
+
// the same module index.js uses, so the hook can never drift from the
|
|
44
|
+
// daemon again. It is fs/os/path-only, keeping index.js out of the
|
|
45
|
+
// hook's require graph (R12), and ships in hookAdapter.js's copy list
|
|
46
|
+
// for the deployed `.claude/hooks/` layout (PR #163).
|
|
47
|
+
var lockFile = lockPaths.getLockFilePath();
|
|
56
48
|
var daemonRunning = false;
|
|
57
49
|
try {
|
|
58
50
|
if (fs.existsSync(lockFile)) {
|
|
59
51
|
var raw = fs.readFileSync(lockFile, 'utf8').trim();
|
|
60
52
|
var payload = raw && raw[0] === '{' ? JSON.parse(raw) : { pid: parseInt(raw, 10) };
|
|
61
53
|
if (payload && payload.pid > 0) {
|
|
54
|
+
// R1: PID-reuse defense. process.kill(pid, 0) only proves SOME
|
|
55
|
+
// process owns that PID -- after macOS sleep / OOM, the kernel may
|
|
56
|
+
// have reused the slain daemon's PID for an unrelated process.
|
|
62
57
|
try { process.kill(payload.pid, 0); daemonRunning = true; } catch (e) {
|
|
63
58
|
// EPERM = process exists but owned by a different user; still a live daemon.
|
|
64
59
|
if (e && e.code === 'EPERM') daemonRunning = true;
|
|
65
60
|
}
|
|
66
|
-
// Lease staleness overrides kill(0)=alive
|
|
67
|
-
// the
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
if (daemonRunning &&
|
|
71
|
-
|
|
72
|
-
var ageMs = Date.now() - fs.statSync(lockFile).mtimeMs;
|
|
73
|
-
if (ageMs > STALE_LOCK_TTL_MS) daemonRunning = false;
|
|
74
|
-
} catch (_) { /* stat failed: leave running flag as-is */ }
|
|
61
|
+
// Lease staleness overrides kill(0)=alive: a lease-aware daemon
|
|
62
|
+
// refreshes the lock mtime on a timer, so an expired lease means
|
|
63
|
+
// dead/wedged regardless of kill(0). Pre-lease locks are never
|
|
64
|
+
// judged stale by mtime (lockIsStaleByLease handles both).
|
|
65
|
+
if (daemonRunning && lockPaths.lockIsStaleByLease(lockFile, payload)) {
|
|
66
|
+
daemonRunning = false;
|
|
75
67
|
}
|
|
76
68
|
}
|
|
77
69
|
}
|