@evomap/evolver 1.69.16 → 1.69.19
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/assets/gep/candidates.jsonl +6 -3
- package/index.js +65 -1
- package/package.json +1 -1
- package/src/atp/autoBuyer.js +242 -0
- package/src/atp/cli.js +248 -0
- package/src/atp/index.js +6 -0
- package/src/evolve.js +1 -1
- package/src/gep/.integrity +0 -0
- package/src/gep/a2aProtocol.js +1 -1
- package/src/gep/candidateEval.js +1 -1
- package/src/gep/candidates.js +1 -1
- package/src/gep/contentHash.js +1 -1
- package/src/gep/crypto.js +1 -1
- package/src/gep/curriculum.js +1 -1
- package/src/gep/deviceId.js +1 -1
- package/src/gep/envFingerprint.js +1 -1
- package/src/gep/explore.js +1 -1
- package/src/gep/hubReview.js +1 -1
- package/src/gep/hubSearch.js +1 -1
- package/src/gep/hubVerify.js +1 -1
- package/src/gep/integrityCheck.js +1 -1
- package/src/gep/learningSignals.js +1 -1
- package/src/gep/memoryGraph.js +1 -1
- package/src/gep/memoryGraphAdapter.js +1 -1
- package/src/gep/mutation.js +1 -1
- package/src/gep/narrativeMemory.js +1 -1
- package/src/gep/personality.js +1 -1
- package/src/gep/policyCheck.js +1 -1
- package/src/gep/prompt.js +1 -1
- package/src/gep/reflection.js +1 -1
- package/src/gep/selector.js +1 -1
- package/src/gep/shield.js +1 -1
- package/src/gep/skillDistiller.js +1 -1
- package/src/gep/solidify.js +1 -1
- package/src/gep/strategy.js +1 -1
- package/src/proxy/lifecycle/manager.js +28 -2
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
{"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-
|
|
2
|
-
{"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-
|
|
3
|
-
{"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-
|
|
1
|
+
{"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-23T06:00:44.714Z","signals":["bounty_task","external_task","human","new","very","grow","how","memory_missing","user_missing","session_logs_missing"],"tags":["bounty_task","external_task","human","new","very","grow","how","memory_missing","user_missing","session_logs_missing","area:orchestration","area:memory"],"shape":{"title":"Harden session log detection and fallback behavior","input":"Recent session transcript + memory snippets + user instructions","output":"A safe, auditable evolution patch guided by GEP assets","invariants":"Protocol order, small reversible patches, validation, append-only events","params":"Signals: bounty_task, external_task, human, new, very, grow, how, memory_missing, user_missing, session_logs_missing","failure_points":"Missing signals, over-broad changes, skipped validation, missing knowledge solidification","evidence":"Signal present: session_logs_missing"}}
|
|
2
|
+
{"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-23T06:00:46.677Z","signals":["memory_missing","user_missing","session_logs_missing"],"tags":["memory_missing","user_missing","session_logs_missing","area:memory"],"shape":{"title":"Harden session log detection and fallback behavior","input":"Recent session transcript + memory snippets + user instructions","output":"A safe, auditable evolution patch guided by GEP assets","invariants":"Protocol order, small reversible patches, validation, append-only events","params":"Signals: memory_missing, user_missing, session_logs_missing","failure_points":"Missing signals, over-broad changes, skipped validation, missing knowledge solidification","evidence":"Signal present: session_logs_missing"}}
|
|
3
|
+
{"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-23T06:00:48.536Z","signals":["memory_missing","user_missing","session_logs_missing"],"tags":["memory_missing","user_missing","session_logs_missing","area:memory"],"shape":{"title":"Harden session log detection and fallback behavior","input":"Recent session transcript + memory snippets + user instructions","output":"A safe, auditable evolution patch guided by GEP assets","invariants":"Protocol order, small reversible patches, validation, append-only events","params":"Signals: memory_missing, user_missing, session_logs_missing","failure_points":"Missing signals, over-broad changes, skipped validation, missing knowledge solidification","evidence":"Signal present: session_logs_missing"}}
|
|
4
|
+
{"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-23T06:01:00.086Z","signals":["bounty_task","external_task","mismatch","components","server","react","troubleshooting","dynamic routes","hydration","React Server Components","memory_missing","user_missing","session_logs_missing"],"tags":["bounty_task","external_task","mismatch","components","server","react","troubleshooting","dynamic routes","hydration","React Server Components","memory_missing","user_missing","session_logs_missing","area:orchestration","area:memory"],"shape":{"title":"Harden session log detection and fallback behavior","input":"Recent session transcript + memory snippets + user instructions","output":"A safe, auditable evolution patch guided by GEP assets","invariants":"Protocol order, small reversible patches, validation, append-only events","params":"Signals: bounty_task, external_task, mismatch, components, server, react, troubleshooting, dynamic routes, hydration, React Server Components, memory_missing, user_missing, session_logs_missing","failure_points":"Missing signals, over-broad changes, skipped validation, missing knowledge solidification","evidence":"Signal present: session_logs_missing"}}
|
|
5
|
+
{"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-23T06:01:02.498Z","signals":["bounty_task","external_task","mismatch","components","server","react","troubleshooting","dynamic routes","hydration","React Server Components","memory_missing","user_missing","session_logs_missing"],"tags":["bounty_task","external_task","mismatch","components","server","react","troubleshooting","dynamic routes","hydration","React Server Components","memory_missing","user_missing","session_logs_missing","area:orchestration","area:memory"],"shape":{"title":"Harden session log detection and fallback behavior","input":"Recent session transcript + memory snippets + user instructions","output":"A safe, auditable evolution patch guided by GEP assets","invariants":"Protocol order, small reversible patches, validation, append-only events","params":"Signals: bounty_task, external_task, mismatch, components, server, react, troubleshooting, dynamic routes, hydration, React Server Components, memory_missing, user_missing, session_logs_missing","failure_points":"Missing signals, over-broad changes, skipped validation, missing knowledge solidification","evidence":"Signal present: session_logs_missing"}}
|
|
6
|
+
{"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-23T06:01:04.594Z","signals":["bounty_task","external_task","what","understand","really","dont","explanation.","memory_missing","user_missing","session_logs_missing"],"tags":["bounty_task","external_task","what","understand","really","dont","explanation.","memory_missing","user_missing","session_logs_missing","area:orchestration","area:memory"],"shape":{"title":"Harden session log detection and fallback behavior","input":"Recent session transcript + memory snippets + user instructions","output":"A safe, auditable evolution patch guided by GEP assets","invariants":"Protocol order, small reversible patches, validation, append-only events","params":"Signals: bounty_task, external_task, what, understand, really, dont, explanation., memory_missing, user_missing, session_logs_missing","failure_points":"Missing signals, over-broad changes, skipped validation, missing knowledge solidification","evidence":"Signal present: session_logs_missing"}}
|
package/index.js
CHANGED
|
@@ -248,6 +248,26 @@ async function main() {
|
|
|
248
248
|
console.warn('[ATP] Auto-init failed: ' + (atpInitErr && atpInitErr.message || atpInitErr));
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
+
// ATP: opt-in capability-gap auto-buyer (default OFF, must be explicitly enabled).
|
|
252
|
+
try {
|
|
253
|
+
const autoBuyRaw = (process.env.EVOLVER_ATP_AUTOBUY || 'off').toLowerCase().trim();
|
|
254
|
+
const autoBuyOn = autoBuyRaw === 'on' || autoBuyRaw === '1' || autoBuyRaw === 'true';
|
|
255
|
+
if (autoBuyOn) {
|
|
256
|
+
const hubUrl = process.env.A2A_HUB_URL || process.env.EVOMAP_HUB_URL || '';
|
|
257
|
+
if (hubUrl) {
|
|
258
|
+
const { autoBuyer } = require('./src/atp');
|
|
259
|
+
autoBuyer.start({
|
|
260
|
+
dailyCap: Number(process.env.ATP_AUTOBUY_DAILY_CAP_CREDITS) || undefined,
|
|
261
|
+
perOrderCap: Number(process.env.ATP_AUTOBUY_PER_ORDER_CAP_CREDITS) || undefined,
|
|
262
|
+
});
|
|
263
|
+
} else {
|
|
264
|
+
console.warn('[ATP-AutoBuyer] EVOLVER_ATP_AUTOBUY=on but no hub URL configured, skipping.');
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
} catch (autoBuyInitErr) {
|
|
268
|
+
console.warn('[ATP-AutoBuyer] Init failed: ' + (autoBuyInitErr && autoBuyInitErr.message || autoBuyInitErr));
|
|
269
|
+
}
|
|
270
|
+
|
|
251
271
|
// Hoist module refs used inside the loop to avoid repeated module lookups per cycle
|
|
252
272
|
const idleScheduler = require('./src/gep/idleScheduler');
|
|
253
273
|
const { shouldDistillFromFailures: shouldDF, autoDistillFromFailures: autoDF } = require('./src/gep/skillDistiller');
|
|
@@ -932,8 +952,36 @@ async function main() {
|
|
|
932
952
|
process.exit(1);
|
|
933
953
|
}
|
|
934
954
|
|
|
955
|
+
} else if (command === 'buy' || command === 'orders' || command === 'verify') {
|
|
956
|
+
try {
|
|
957
|
+
const atpCli = require('./src/atp/cli');
|
|
958
|
+
const subArgs = args.slice(); // args already exclude the command token
|
|
959
|
+
let parsed;
|
|
960
|
+
let runner;
|
|
961
|
+
if (command === 'buy') {
|
|
962
|
+
parsed = atpCli.parseBuyArgs(subArgs);
|
|
963
|
+
runner = atpCli.runBuy;
|
|
964
|
+
} else if (command === 'orders') {
|
|
965
|
+
parsed = atpCli.parseOrdersArgs(subArgs);
|
|
966
|
+
runner = atpCli.runOrders;
|
|
967
|
+
} else {
|
|
968
|
+
parsed = atpCli.parseVerifyArgs(subArgs);
|
|
969
|
+
runner = atpCli.runVerify;
|
|
970
|
+
}
|
|
971
|
+
if (!parsed.ok) {
|
|
972
|
+
console.error('[ATP] ' + parsed.error);
|
|
973
|
+
console.error(atpCli.printUsage());
|
|
974
|
+
process.exit(2);
|
|
975
|
+
}
|
|
976
|
+
const res = await runner(parsed.opts);
|
|
977
|
+
process.exit(res && typeof res.exitCode === 'number' ? res.exitCode : 0);
|
|
978
|
+
} catch (atpCliErr) {
|
|
979
|
+
console.error('[ATP] CLI error:', atpCliErr && atpCliErr.message || atpCliErr);
|
|
980
|
+
process.exit(1);
|
|
981
|
+
}
|
|
982
|
+
|
|
935
983
|
} else {
|
|
936
|
-
console.log(`Usage: node index.js [run|/evolve|solidify|review|distill|fetch|asset-log|setup-hooks] [--loop]
|
|
984
|
+
console.log(`Usage: node index.js [run|/evolve|solidify|review|distill|fetch|asset-log|setup-hooks|buy|orders|verify] [--loop]
|
|
937
985
|
- fetch flags:
|
|
938
986
|
- --skill=<id> | -s <id> (skill ID to download)
|
|
939
987
|
- --out=<dir> (output directory, default: ./skills/<skill_id>)
|
|
@@ -958,6 +1006,22 @@ async function main() {
|
|
|
958
1006
|
- --since=<ISO_date> (entries after date)
|
|
959
1007
|
- --json (raw JSON output)
|
|
960
1008
|
|
|
1009
|
+
ATP (Agent Transaction Protocol) subcommands:
|
|
1010
|
+
- buy <caps> (place an ATP order; caps is comma-separated)
|
|
1011
|
+
- --budget=<N> (credits to spend, default 10)
|
|
1012
|
+
- --question="..." (order description)
|
|
1013
|
+
- --routing=<mode> (fastest|cheapest|auction|swarm, default fastest)
|
|
1014
|
+
- --verify=<mode> (auto|ai_judge|bilateral, default auto)
|
|
1015
|
+
- --no-wait (return immediately after placing)
|
|
1016
|
+
- --timeout=<seconds> (lifecycle timeout, default 300)
|
|
1017
|
+
- orders (list your recent ATP orders / deliveries)
|
|
1018
|
+
- --role=consumer|merchant (default consumer)
|
|
1019
|
+
- --status=pending|verified|disputed|settled
|
|
1020
|
+
- --limit=<N> (1..100, default 20)
|
|
1021
|
+
- --json (raw JSON)
|
|
1022
|
+
- verify <orderId> (confirm delivery or trigger AI judge)
|
|
1023
|
+
- --action=confirm|ai_judge (default confirm)
|
|
1024
|
+
|
|
961
1025
|
Validator role (decentralized validation, default ON since v1.69.0):
|
|
962
1026
|
- EVOLVER_VALIDATOR_ENABLED=0 opt out (env beats persisted flag and default)
|
|
963
1027
|
- EVOLVER_VALIDATOR_ENABLED=1 explicitly opt in
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evomap/evolver",
|
|
3
|
-
"version": "1.69.
|
|
3
|
+
"version": "1.69.19",
|
|
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": {
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
// ATP Auto-Buyer (opt-in)
|
|
2
|
+
// Converts capability gaps into ATP orders with strict budget caps and
|
|
3
|
+
// 24h question-level deduplication. Default OFF; enabled by setting
|
|
4
|
+
// EVOLVER_ATP_AUTOBUY=on (also accepts 1/true). Budget caps:
|
|
5
|
+
// ATP_AUTOBUY_DAILY_CAP_CREDITS (default 50)
|
|
6
|
+
// ATP_AUTOBUY_PER_ORDER_CAP_CREDITS (default 10)
|
|
7
|
+
// Cold-start safety: the first 5 minutes after process start use a half-cap
|
|
8
|
+
// to protect against misconfiguration loops on restart storms.
|
|
9
|
+
//
|
|
10
|
+
// Integration contract:
|
|
11
|
+
// 1) Call start({ dailyCap, perOrderCap }) once when EVOLVER_ATP_AUTOBUY=on.
|
|
12
|
+
// 2) Call considerOrder({ signals, question, capabilities, budget, ... })
|
|
13
|
+
// from the evolve loop whenever a capability gap is detected.
|
|
14
|
+
// 3) Result shape: { ok, skipped?, reason?, data?, error? }.
|
|
15
|
+
//
|
|
16
|
+
// Failure modes are non-fatal; every external call is wrapped with a 3s
|
|
17
|
+
// timeout race so loop cadence is never blocked by network issues.
|
|
18
|
+
|
|
19
|
+
const crypto = require('crypto');
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
|
|
23
|
+
const { getMemoryDir } = require('../gep/paths');
|
|
24
|
+
const hubClient = require('./hubClient');
|
|
25
|
+
|
|
26
|
+
const DEFAULT_DAILY_CAP = 50;
|
|
27
|
+
const DEFAULT_PER_ORDER_CAP = 10;
|
|
28
|
+
const DEFAULT_ORDER_TIMEOUT_MS = 3000;
|
|
29
|
+
const COLD_START_WINDOW_MS = 5 * 60 * 1000;
|
|
30
|
+
const DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
|
|
31
|
+
const LEDGER_FILENAME = 'atp-autobuyer-ledger.json';
|
|
32
|
+
|
|
33
|
+
let _started = false;
|
|
34
|
+
let _startedAt = 0;
|
|
35
|
+
let _config = {
|
|
36
|
+
dailyCap: DEFAULT_DAILY_CAP,
|
|
37
|
+
perOrderCap: DEFAULT_PER_ORDER_CAP,
|
|
38
|
+
timeoutMs: DEFAULT_ORDER_TIMEOUT_MS,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function _ledgerPath() {
|
|
42
|
+
return path.join(getMemoryDir(), LEDGER_FILENAME);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function _todayKey(now) {
|
|
46
|
+
const d = new Date(typeof now === 'number' ? now : Date.now());
|
|
47
|
+
return d.toISOString().slice(0, 10); // UTC YYYY-MM-DD
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function _isEnabled() {
|
|
51
|
+
const raw = (process.env.EVOLVER_ATP_AUTOBUY || 'off').toLowerCase().trim();
|
|
52
|
+
return raw === 'on' || raw === '1' || raw === 'true';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function _emptyLedger() {
|
|
56
|
+
return { version: 1, dayKey: _todayKey(), spent: 0, dedup: {} };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function _readLedger() {
|
|
60
|
+
try {
|
|
61
|
+
const p = _ledgerPath();
|
|
62
|
+
if (!fs.existsSync(p)) return _emptyLedger();
|
|
63
|
+
const raw = fs.readFileSync(p, 'utf8');
|
|
64
|
+
const parsed = JSON.parse(raw);
|
|
65
|
+
if (!parsed || typeof parsed !== 'object') return _emptyLedger();
|
|
66
|
+
if (!parsed.dayKey || !parsed.dedup) return _emptyLedger();
|
|
67
|
+
return parsed;
|
|
68
|
+
} catch (_) {
|
|
69
|
+
return _emptyLedger();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function _writeLedger(ledger) {
|
|
74
|
+
try {
|
|
75
|
+
const dir = getMemoryDir();
|
|
76
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
77
|
+
const tmp = _ledgerPath() + '.tmp';
|
|
78
|
+
fs.writeFileSync(tmp, JSON.stringify(ledger, null, 2));
|
|
79
|
+
fs.renameSync(tmp, _ledgerPath());
|
|
80
|
+
} catch (_) {
|
|
81
|
+
// Non-fatal: ledger persistence failure means next process restart
|
|
82
|
+
// re-reads previous ledger (so existing caps still apply).
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function _rotateIfNewDay(ledger, now) {
|
|
87
|
+
const today = _todayKey(now);
|
|
88
|
+
if (ledger.dayKey !== today) {
|
|
89
|
+
return { version: 1, dayKey: today, spent: 0, dedup: ledger.dedup || {} };
|
|
90
|
+
}
|
|
91
|
+
return ledger;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function _pruneDedup(ledger, now) {
|
|
95
|
+
const cutoff = (typeof now === 'number' ? now : Date.now()) - DEDUP_TTL_MS;
|
|
96
|
+
const out = {};
|
|
97
|
+
const src = ledger.dedup || {};
|
|
98
|
+
const keys = Object.keys(src);
|
|
99
|
+
for (let i = 0; i < keys.length; i++) {
|
|
100
|
+
const k = keys[i];
|
|
101
|
+
if (typeof src[k] === 'number' && src[k] >= cutoff) out[k] = src[k];
|
|
102
|
+
}
|
|
103
|
+
ledger.dedup = out;
|
|
104
|
+
return ledger;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function _questionHash(opts) {
|
|
108
|
+
const caps = Array.isArray(opts.capabilities) ? opts.capabilities.slice().sort().join(',') : '';
|
|
109
|
+
const q = (opts.question || '').slice(0, 2000);
|
|
110
|
+
return crypto.createHash('sha256').update(caps + '|' + q).digest('hex').slice(0, 24);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function _effectiveCap(value, now) {
|
|
114
|
+
const n = Number(value);
|
|
115
|
+
if (!Number.isFinite(n) || n < 0) return 0;
|
|
116
|
+
const within = _startedAt > 0 && (now - _startedAt) < COLD_START_WINDOW_MS;
|
|
117
|
+
return within ? Math.floor(n / 2) : Math.floor(n);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function start(opts) {
|
|
121
|
+
if (_started) return;
|
|
122
|
+
if (!_isEnabled()) return;
|
|
123
|
+
_started = true;
|
|
124
|
+
_startedAt = Date.now();
|
|
125
|
+
const dailyCap = Math.max(0, Math.floor(Number((opts && opts.dailyCap) || process.env.ATP_AUTOBUY_DAILY_CAP_CREDITS) || DEFAULT_DAILY_CAP));
|
|
126
|
+
const perOrderCap = Math.max(0, Math.floor(Number((opts && opts.perOrderCap) || process.env.ATP_AUTOBUY_PER_ORDER_CAP_CREDITS) || DEFAULT_PER_ORDER_CAP));
|
|
127
|
+
const timeoutMs = Math.max(500, Math.floor(Number((opts && opts.timeoutMs) || DEFAULT_ORDER_TIMEOUT_MS)));
|
|
128
|
+
_config = { dailyCap, perOrderCap, timeoutMs };
|
|
129
|
+
let ledger = _readLedger();
|
|
130
|
+
ledger = _rotateIfNewDay(ledger, _startedAt);
|
|
131
|
+
ledger = _pruneDedup(ledger, _startedAt);
|
|
132
|
+
_writeLedger(ledger);
|
|
133
|
+
console.log('[ATP-AutoBuyer] Started (dailyCap=' + dailyCap + ', perOrderCap=' + perOrderCap + ', cold-start half-cap for ' + (COLD_START_WINDOW_MS / 1000) + 's)');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function stop() {
|
|
137
|
+
_started = false;
|
|
138
|
+
_startedAt = 0;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function isStarted() {
|
|
142
|
+
return _started;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function _withTimeout(promise, timeoutMs) {
|
|
146
|
+
return Promise.race([
|
|
147
|
+
promise,
|
|
148
|
+
new Promise(function (resolve) {
|
|
149
|
+
setTimeout(function () { resolve({ ok: false, error: 'autobuyer_timeout' }); }, timeoutMs);
|
|
150
|
+
}),
|
|
151
|
+
]);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function considerOrder(opts) {
|
|
155
|
+
if (!_started) return { ok: false, skipped: true, reason: 'not_started' };
|
|
156
|
+
if (!opts || !Array.isArray(opts.capabilities) || opts.capabilities.length === 0) {
|
|
157
|
+
return { ok: false, skipped: true, reason: 'no_capabilities' };
|
|
158
|
+
}
|
|
159
|
+
const now = Date.now();
|
|
160
|
+
let ledger = _readLedger();
|
|
161
|
+
ledger = _rotateIfNewDay(ledger, now);
|
|
162
|
+
ledger = _pruneDedup(ledger, now);
|
|
163
|
+
|
|
164
|
+
const hash = _questionHash(opts);
|
|
165
|
+
if (ledger.dedup[hash]) {
|
|
166
|
+
return { ok: false, skipped: true, reason: 'dedup_hit', hash: hash };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const dailyCap = _effectiveCap(_config.dailyCap, now);
|
|
170
|
+
const perOrderCap = _effectiveCap(_config.perOrderCap, now);
|
|
171
|
+
const remaining = Math.max(0, dailyCap - (ledger.spent || 0));
|
|
172
|
+
if (remaining <= 0) {
|
|
173
|
+
console.warn('[ATP-AutoBuyer] Daily cap reached, skipping order (spent=' + ledger.spent + ', cap=' + dailyCap + ')');
|
|
174
|
+
return { ok: false, skipped: true, reason: 'daily_cap_reached', spent: ledger.spent, cap: dailyCap };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const requested = Math.max(1, Math.floor(Number(opts.budget) || perOrderCap));
|
|
178
|
+
const budget = Math.min(requested, perOrderCap, remaining);
|
|
179
|
+
if (budget <= 0) {
|
|
180
|
+
return { ok: false, skipped: true, reason: 'budget_clamped_to_zero' };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const orderOpts = {
|
|
184
|
+
capabilities: opts.capabilities,
|
|
185
|
+
budget: budget,
|
|
186
|
+
routingMode: opts.routingMode || 'fastest',
|
|
187
|
+
verifyMode: opts.verifyMode || 'auto',
|
|
188
|
+
question: opts.question,
|
|
189
|
+
signals: opts.signals,
|
|
190
|
+
minReputation: opts.minReputation,
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const result = await _withTimeout(hubClient.placeOrder(orderOpts), _config.timeoutMs);
|
|
194
|
+
|
|
195
|
+
if (result && result.ok) {
|
|
196
|
+
ledger.spent = (ledger.spent || 0) + budget;
|
|
197
|
+
ledger.dedup[hash] = now;
|
|
198
|
+
_writeLedger(ledger);
|
|
199
|
+
console.log('[ATP-AutoBuyer] Order placed: ' + (result.data && result.data.order_id) + ' budget=' + budget + ' remaining_today=' + Math.max(0, dailyCap - ledger.spent));
|
|
200
|
+
return { ok: true, data: result.data, spent: budget };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// On failure still record dedup so we don't hammer the hub for the same
|
|
204
|
+
// capability gap within the TTL window (but do NOT charge the spend).
|
|
205
|
+
ledger.dedup[hash] = now;
|
|
206
|
+
_writeLedger(ledger);
|
|
207
|
+
return { ok: false, error: (result && result.error) || 'unknown_error' };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Test-only reset, not exported by default.
|
|
211
|
+
function _resetForTests() {
|
|
212
|
+
_started = false;
|
|
213
|
+
_startedAt = 0;
|
|
214
|
+
_config = {
|
|
215
|
+
dailyCap: DEFAULT_DAILY_CAP,
|
|
216
|
+
perOrderCap: DEFAULT_PER_ORDER_CAP,
|
|
217
|
+
timeoutMs: DEFAULT_ORDER_TIMEOUT_MS,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
module.exports = {
|
|
222
|
+
start,
|
|
223
|
+
stop,
|
|
224
|
+
isStarted,
|
|
225
|
+
considerOrder,
|
|
226
|
+
// Exposed for tests and diagnostics only; callers should not depend on
|
|
227
|
+
// these internals in production code paths.
|
|
228
|
+
__internals: {
|
|
229
|
+
readLedger: _readLedger,
|
|
230
|
+
writeLedger: _writeLedger,
|
|
231
|
+
questionHash: _questionHash,
|
|
232
|
+
effectiveCap: _effectiveCap,
|
|
233
|
+
resetForTests: _resetForTests,
|
|
234
|
+
constants: {
|
|
235
|
+
DEFAULT_DAILY_CAP,
|
|
236
|
+
DEFAULT_PER_ORDER_CAP,
|
|
237
|
+
COLD_START_WINDOW_MS,
|
|
238
|
+
DEDUP_TTL_MS,
|
|
239
|
+
LEDGER_FILENAME,
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
};
|
package/src/atp/cli.js
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
// ATP CLI: lightweight argument parsers and runners for the `buy`, `orders`,
|
|
2
|
+
// and `verify` subcommands. Separated from index.js so they can be unit-tested
|
|
3
|
+
// without booting the full evolver loop.
|
|
4
|
+
//
|
|
5
|
+
// Public API:
|
|
6
|
+
// parseBuyArgs(args) -> { ok, opts?, error? }
|
|
7
|
+
// parseOrdersArgs(args)-> { ok, opts?, error? }
|
|
8
|
+
// parseVerifyArgs(args)-> { ok, opts?, error? }
|
|
9
|
+
// runBuy(opts, deps)
|
|
10
|
+
// runOrders(opts, deps)
|
|
11
|
+
// runVerify(opts, deps)
|
|
12
|
+
//
|
|
13
|
+
// `deps` is an object containing the ATP module (defaults to require('./index')),
|
|
14
|
+
// injectable for tests. Each runner returns a Promise that resolves to
|
|
15
|
+
// { exitCode: number, output?: string, data?: object }.
|
|
16
|
+
|
|
17
|
+
function _parseNamed(args, longFlag, shortFlag) {
|
|
18
|
+
const long = args.findIndex(a => typeof a === 'string' && (a === longFlag || a.startsWith(longFlag + '=')));
|
|
19
|
+
if (long !== -1) {
|
|
20
|
+
const token = args[long];
|
|
21
|
+
if (token.startsWith(longFlag + '=')) return token.slice(longFlag.length + 1);
|
|
22
|
+
if (args[long + 1] && !String(args[long + 1]).startsWith('--')) return args[long + 1];
|
|
23
|
+
}
|
|
24
|
+
if (shortFlag) {
|
|
25
|
+
const short = args.indexOf(shortFlag);
|
|
26
|
+
if (short !== -1 && args[short + 1] && !String(args[short + 1]).startsWith('--')) return args[short + 1];
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function _toBool(v) {
|
|
32
|
+
if (typeof v !== 'string') return false;
|
|
33
|
+
const s = v.toLowerCase();
|
|
34
|
+
return s === '1' || s === 'true' || s === 'yes' || s === 'on';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function parseBuyArgs(args) {
|
|
38
|
+
if (!Array.isArray(args) || args.length === 0) {
|
|
39
|
+
return { ok: false, error: 'buy requires <capabilities>: comma-separated list (e.g. code_review,bug_fix)' };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// First positional (not starting with --) is the capability list.
|
|
43
|
+
let capabilitiesRaw = null;
|
|
44
|
+
for (let i = 0; i < args.length; i++) {
|
|
45
|
+
const a = args[i];
|
|
46
|
+
if (typeof a === 'string' && !a.startsWith('--')) {
|
|
47
|
+
capabilitiesRaw = a;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (!capabilitiesRaw) {
|
|
52
|
+
return { ok: false, error: 'buy requires <capabilities>: comma-separated list' };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const capabilities = capabilitiesRaw.split(',').map(s => s.trim()).filter(Boolean);
|
|
56
|
+
if (capabilities.length === 0) {
|
|
57
|
+
return { ok: false, error: 'no valid capabilities parsed from: ' + capabilitiesRaw };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const budgetRaw = _parseNamed(args, '--budget', '-b');
|
|
61
|
+
const budget = Math.max(1, Math.round(Number(budgetRaw) || 10));
|
|
62
|
+
|
|
63
|
+
const question = _parseNamed(args, '--question', '-q') || '';
|
|
64
|
+
const routingMode = _parseNamed(args, '--routing', null) || 'fastest';
|
|
65
|
+
const verifyMode = _parseNamed(args, '--verify', null) || 'auto';
|
|
66
|
+
const noWait = args.includes('--no-wait');
|
|
67
|
+
const timeoutMsRaw = _parseNamed(args, '--timeout', null);
|
|
68
|
+
const timeoutMs = timeoutMsRaw ? Math.max(1000, Math.round(Number(timeoutMsRaw) * 1000)) : 300000;
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
ok: true,
|
|
72
|
+
opts: {
|
|
73
|
+
capabilities,
|
|
74
|
+
budget,
|
|
75
|
+
question,
|
|
76
|
+
routingMode,
|
|
77
|
+
verifyMode,
|
|
78
|
+
noWait,
|
|
79
|
+
timeoutMs,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function parseOrdersArgs(args) {
|
|
85
|
+
const role = _parseNamed(args, '--role', null);
|
|
86
|
+
if (role && !['consumer', 'merchant'].includes(role)) {
|
|
87
|
+
return { ok: false, error: 'invalid --role: ' + role + ' (expected consumer|merchant)' };
|
|
88
|
+
}
|
|
89
|
+
const status = _parseNamed(args, '--status', null);
|
|
90
|
+
if (status && !['pending', 'verified', 'disputed', 'settled'].includes(status)) {
|
|
91
|
+
return { ok: false, error: 'invalid --status: ' + status };
|
|
92
|
+
}
|
|
93
|
+
const limitRaw = _parseNamed(args, '--limit', null);
|
|
94
|
+
const limit = limitRaw ? Math.max(1, Math.min(100, Math.round(Number(limitRaw)))) : 20;
|
|
95
|
+
const jsonOut = args.includes('--json');
|
|
96
|
+
return {
|
|
97
|
+
ok: true,
|
|
98
|
+
opts: { role: role || 'consumer', status: status || null, limit, jsonOut },
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function parseVerifyArgs(args) {
|
|
103
|
+
let orderId = null;
|
|
104
|
+
for (let i = 0; i < args.length; i++) {
|
|
105
|
+
const a = args[i];
|
|
106
|
+
if (typeof a === 'string' && !a.startsWith('--')) {
|
|
107
|
+
orderId = a;
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (!orderId) {
|
|
112
|
+
return { ok: false, error: 'verify requires <orderId>' };
|
|
113
|
+
}
|
|
114
|
+
const action = _parseNamed(args, '--action', null) || 'confirm';
|
|
115
|
+
if (!['confirm', 'ai_judge'].includes(action)) {
|
|
116
|
+
return { ok: false, error: 'invalid --action: ' + action + ' (expected confirm|ai_judge)' };
|
|
117
|
+
}
|
|
118
|
+
return { ok: true, opts: { orderId, action } };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function runBuy(opts, deps) {
|
|
122
|
+
const atp = (deps && deps.atp) || require('./index');
|
|
123
|
+
const consumerAgent = atp.consumerAgent;
|
|
124
|
+
const log = (deps && deps.log) || console.log;
|
|
125
|
+
const err = (deps && deps.err) || console.error;
|
|
126
|
+
|
|
127
|
+
log('[ATP] Placing order: capabilities=' + opts.capabilities.join(',') + ' budget=' + opts.budget + ' mode=' + opts.routingMode);
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
if (opts.noWait) {
|
|
131
|
+
const result = await consumerAgent.orderService({
|
|
132
|
+
capabilities: opts.capabilities,
|
|
133
|
+
budget: opts.budget,
|
|
134
|
+
routingMode: opts.routingMode,
|
|
135
|
+
verifyMode: opts.verifyMode,
|
|
136
|
+
question: opts.question,
|
|
137
|
+
});
|
|
138
|
+
if (!result.ok) {
|
|
139
|
+
err('[ATP] Order failed: ' + (result.error || 'unknown'));
|
|
140
|
+
return { exitCode: 1, data: result };
|
|
141
|
+
}
|
|
142
|
+
log('[ATP] Order placed: ' + result.data.order_id);
|
|
143
|
+
return { exitCode: 0, data: result.data };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const result = await consumerAgent.orderAndWait({
|
|
147
|
+
capabilities: opts.capabilities,
|
|
148
|
+
budget: opts.budget,
|
|
149
|
+
routingMode: opts.routingMode,
|
|
150
|
+
verifyMode: opts.verifyMode,
|
|
151
|
+
question: opts.question,
|
|
152
|
+
timeoutMs: opts.timeoutMs,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
if (!result.ok) {
|
|
156
|
+
err('[ATP] Order lifecycle failed: ' + (result.error || 'unknown'));
|
|
157
|
+
return { exitCode: 1, data: result };
|
|
158
|
+
}
|
|
159
|
+
log('[ATP] Order settled: ' + (result.order && result.order.order_id));
|
|
160
|
+
if (result.finalStatus) {
|
|
161
|
+
log('[ATP] Final status: ' + JSON.stringify(result.finalStatus, null, 2));
|
|
162
|
+
}
|
|
163
|
+
return { exitCode: 0, data: result };
|
|
164
|
+
} catch (e) {
|
|
165
|
+
err('[ATP] buy error: ' + (e && e.message || e));
|
|
166
|
+
return { exitCode: 1, error: String(e) };
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function runOrders(opts, deps) {
|
|
171
|
+
const atp = (deps && deps.atp) || require('./index');
|
|
172
|
+
const hubClient = atp.hubClient;
|
|
173
|
+
const log = (deps && deps.log) || console.log;
|
|
174
|
+
const err = (deps && deps.err) || console.error;
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
const result = await hubClient.listProofs({
|
|
178
|
+
role: opts.role,
|
|
179
|
+
status: opts.status || undefined,
|
|
180
|
+
limit: opts.limit,
|
|
181
|
+
});
|
|
182
|
+
if (!result.ok) {
|
|
183
|
+
err('[ATP] listProofs failed: ' + (result.error || 'unknown'));
|
|
184
|
+
return { exitCode: 1, data: result };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const proofs = (result.data && result.data.proofs) || result.data || [];
|
|
188
|
+
if (opts.jsonOut) {
|
|
189
|
+
log(JSON.stringify(proofs, null, 2));
|
|
190
|
+
return { exitCode: 0, data: proofs };
|
|
191
|
+
}
|
|
192
|
+
if (!Array.isArray(proofs) || proofs.length === 0) {
|
|
193
|
+
log('[ATP] No orders found for role=' + opts.role + (opts.status ? ' status=' + opts.status : ''));
|
|
194
|
+
return { exitCode: 0, data: [] };
|
|
195
|
+
}
|
|
196
|
+
log('[ATP] Showing ' + proofs.length + ' order(s):');
|
|
197
|
+
for (const p of proofs) {
|
|
198
|
+
const when = p.createdAt || p.created_at || 'unknown';
|
|
199
|
+
log(' - ' + (p.taskId || p.order_id || p.id) + ' | status=' + p.status + ' | created=' + when);
|
|
200
|
+
}
|
|
201
|
+
return { exitCode: 0, data: proofs };
|
|
202
|
+
} catch (e) {
|
|
203
|
+
err('[ATP] orders error: ' + (e && e.message || e));
|
|
204
|
+
return { exitCode: 1, error: String(e) };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function runVerify(opts, deps) {
|
|
209
|
+
const atp = (deps && deps.atp) || require('./index');
|
|
210
|
+
const consumerAgent = atp.consumerAgent;
|
|
211
|
+
const log = (deps && deps.log) || console.log;
|
|
212
|
+
const err = (deps && deps.err) || console.error;
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const fn = opts.action === 'ai_judge' ? consumerAgent.requestAiJudge : consumerAgent.confirmDelivery;
|
|
216
|
+
const result = await fn(opts.orderId);
|
|
217
|
+
if (!result.ok) {
|
|
218
|
+
err('[ATP] verify failed: ' + (result.error || 'unknown'));
|
|
219
|
+
return { exitCode: 1, data: result };
|
|
220
|
+
}
|
|
221
|
+
log('[ATP] verify ok (' + opts.action + '): ' + JSON.stringify(result.data));
|
|
222
|
+
return { exitCode: 0, data: result.data };
|
|
223
|
+
} catch (e) {
|
|
224
|
+
err('[ATP] verify error: ' + (e && e.message || e));
|
|
225
|
+
return { exitCode: 1, error: String(e) };
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function printUsage() {
|
|
230
|
+
return [
|
|
231
|
+
'ATP subcommands:',
|
|
232
|
+
' evolver buy <caps> [--budget=N] [--question "..."] [--routing=fastest|cheapest|auction|swarm]',
|
|
233
|
+
' [--verify=auto|ai_judge|bilateral] [--no-wait] [--timeout=<seconds>]',
|
|
234
|
+
' evolver orders [--role=consumer|merchant] [--status=pending|verified|disputed|settled]',
|
|
235
|
+
' [--limit=N] [--json]',
|
|
236
|
+
' evolver verify <orderId> [--action=confirm|ai_judge]',
|
|
237
|
+
].join('\n');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
module.exports = {
|
|
241
|
+
parseBuyArgs,
|
|
242
|
+
parseOrdersArgs,
|
|
243
|
+
parseVerifyArgs,
|
|
244
|
+
runBuy,
|
|
245
|
+
runOrders,
|
|
246
|
+
runVerify,
|
|
247
|
+
printUsage,
|
|
248
|
+
};
|
package/src/atp/index.js
CHANGED
|
@@ -7,12 +7,16 @@
|
|
|
7
7
|
// consumerAgent - ready-to-use consumer agent template
|
|
8
8
|
// serviceHelper - service publishing helper
|
|
9
9
|
// defaultHandler - default order handler + config helpers for auto-ATP
|
|
10
|
+
// autoBuyer - opt-in capability-gap auto order helper with budget caps
|
|
11
|
+
// cli - parsers and runners for the `buy`/`orders`/`verify` subcommands
|
|
10
12
|
|
|
11
13
|
const hubClient = require('./hubClient');
|
|
12
14
|
const merchantAgent = require('./merchantAgent');
|
|
13
15
|
const consumerAgent = require('./consumerAgent');
|
|
14
16
|
const serviceHelper = require('./serviceHelper');
|
|
15
17
|
const defaultHandler = require('./defaultHandler');
|
|
18
|
+
const autoBuyer = require('./autoBuyer');
|
|
19
|
+
const cli = require('./cli');
|
|
16
20
|
|
|
17
21
|
module.exports = {
|
|
18
22
|
hubClient,
|
|
@@ -20,4 +24,6 @@ module.exports = {
|
|
|
20
24
|
consumerAgent,
|
|
21
25
|
serviceHelper,
|
|
22
26
|
defaultHandler,
|
|
27
|
+
autoBuyer,
|
|
28
|
+
cli,
|
|
23
29
|
};
|