@evomap/evolver 1.69.16 → 1.69.20

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.
@@ -1,3 +1,3 @@
1
- {"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-22T11:22:20.060Z","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"}}
2
- {"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-22T11:22:21.928Z","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-22T11:22:23.784Z","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"}}
1
+ {"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-23T16:59:44.501Z","signals":["bounty_task","external_task","results","professional-level","achieve","how","strategy-list","ratio","aspect","letterboxing","cinematic","editing","video","video-editing","memory_missing","user_missing","session_logs_missing"],"tags":["bounty_task","external_task","results","professional-level","achieve","how","strategy-list","ratio","aspect","letterboxing","cinematic","editing","video","video-editing","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, results, professional-level, achieve, how, strategy-list, ratio, aspect, letterboxing, cinematic, editing, video, video-editing, 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-23T16:59:46.705Z","signals":["bounty_task","external_task","results","professional-level","achieve","how","strategy-list","ratio","aspect","letterboxing","cinematic","editing","video","video-editing","memory_missing","user_missing","session_logs_missing"],"tags":["bounty_task","external_task","results","professional-level","achieve","how","strategy-list","ratio","aspect","letterboxing","cinematic","editing","video","video-editing","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, results, professional-level, achieve, how, strategy-list, ratio, aspect, letterboxing, cinematic, editing, video, video-editing, 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-23T16:59:48.705Z","signals":["bounty_task","external_task","results","professional-level","achieve","how","strategy-list","ratio","aspect","letterboxing","cinematic","editing","video","video-editing","memory_missing","user_missing","session_logs_missing"],"tags":["bounty_task","external_task","results","professional-level","achieve","how","strategy-list","ratio","aspect","letterboxing","cinematic","editing","video","video-editing","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, results, professional-level, achieve, how, strategy-list, ratio, aspect, letterboxing, cinematic, editing, video, video-editing, 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
@@ -143,6 +143,24 @@ async function main() {
143
143
  }
144
144
 
145
145
  console.log('Starting evolver...');
146
+
147
+ // Preflight: fail fast if git is not on PATH. On Windows in particular
148
+ // a missing git binary can cause evolver to hang silently (see #394),
149
+ // because several cycle-critical steps shell out to git early (repo
150
+ // resolution, diff, blast-radius). Catching this up front makes the
151
+ // failure mode obvious.
152
+ try {
153
+ const { execSync } = require('child_process');
154
+ execSync('git --version', { stdio: 'ignore', timeout: 5000 });
155
+ } catch (_gitErr) {
156
+ console.error('');
157
+ console.error('[Preflight] Could not run "git --version". Evolver requires git to be installed and available on PATH.');
158
+ console.error('[Preflight] On Windows: install Git from https://git-scm.com/download/win and make sure `git --version` works in a fresh terminal.');
159
+ console.error('[Preflight] On macOS: xcode-select --install (or `brew install git`)');
160
+ console.error('[Preflight] On Linux: sudo apt-get install -y git (or your distro equivalent)');
161
+ console.error('');
162
+ process.exit(1);
163
+ }
146
164
 
147
165
  if (isLoop) {
148
166
  // Internal daemon loop (no wrapper required).
@@ -248,6 +266,26 @@ async function main() {
248
266
  console.warn('[ATP] Auto-init failed: ' + (atpInitErr && atpInitErr.message || atpInitErr));
249
267
  }
250
268
 
269
+ // ATP: opt-in capability-gap auto-buyer (default OFF, must be explicitly enabled).
270
+ try {
271
+ const autoBuyRaw = (process.env.EVOLVER_ATP_AUTOBUY || 'off').toLowerCase().trim();
272
+ const autoBuyOn = autoBuyRaw === 'on' || autoBuyRaw === '1' || autoBuyRaw === 'true';
273
+ if (autoBuyOn) {
274
+ const hubUrl = process.env.A2A_HUB_URL || process.env.EVOMAP_HUB_URL || '';
275
+ if (hubUrl) {
276
+ const { autoBuyer } = require('./src/atp');
277
+ autoBuyer.start({
278
+ dailyCap: Number(process.env.ATP_AUTOBUY_DAILY_CAP_CREDITS) || undefined,
279
+ perOrderCap: Number(process.env.ATP_AUTOBUY_PER_ORDER_CAP_CREDITS) || undefined,
280
+ });
281
+ } else {
282
+ console.warn('[ATP-AutoBuyer] EVOLVER_ATP_AUTOBUY=on but no hub URL configured, skipping.');
283
+ }
284
+ }
285
+ } catch (autoBuyInitErr) {
286
+ console.warn('[ATP-AutoBuyer] Init failed: ' + (autoBuyInitErr && autoBuyInitErr.message || autoBuyInitErr));
287
+ }
288
+
251
289
  // Hoist module refs used inside the loop to avoid repeated module lookups per cycle
252
290
  const idleScheduler = require('./src/gep/idleScheduler');
253
291
  const { shouldDistillFromFailures: shouldDF, autoDistillFromFailures: autoDF } = require('./src/gep/skillDistiller');
@@ -932,8 +970,36 @@ async function main() {
932
970
  process.exit(1);
933
971
  }
934
972
 
973
+ } else if (command === 'buy' || command === 'orders' || command === 'verify') {
974
+ try {
975
+ const atpCli = require('./src/atp/cli');
976
+ const subArgs = args.slice(1); // drop the command token (e.g. "buy") itself
977
+ let parsed;
978
+ let runner;
979
+ if (command === 'buy') {
980
+ parsed = atpCli.parseBuyArgs(subArgs);
981
+ runner = atpCli.runBuy;
982
+ } else if (command === 'orders') {
983
+ parsed = atpCli.parseOrdersArgs(subArgs);
984
+ runner = atpCli.runOrders;
985
+ } else {
986
+ parsed = atpCli.parseVerifyArgs(subArgs);
987
+ runner = atpCli.runVerify;
988
+ }
989
+ if (!parsed.ok) {
990
+ console.error('[ATP] ' + parsed.error);
991
+ console.error(atpCli.printUsage());
992
+ process.exit(2);
993
+ }
994
+ const res = await runner(parsed.opts);
995
+ process.exit(res && typeof res.exitCode === 'number' ? res.exitCode : 0);
996
+ } catch (atpCliErr) {
997
+ console.error('[ATP] CLI error:', atpCliErr && atpCliErr.message || atpCliErr);
998
+ process.exit(1);
999
+ }
1000
+
935
1001
  } else {
936
- console.log(`Usage: node index.js [run|/evolve|solidify|review|distill|fetch|asset-log|setup-hooks] [--loop]
1002
+ console.log(`Usage: node index.js [run|/evolve|solidify|review|distill|fetch|asset-log|setup-hooks|buy|orders|verify] [--loop]
937
1003
  - fetch flags:
938
1004
  - --skill=<id> | -s <id> (skill ID to download)
939
1005
  - --out=<dir> (output directory, default: ./skills/<skill_id>)
@@ -958,6 +1024,22 @@ async function main() {
958
1024
  - --since=<ISO_date> (entries after date)
959
1025
  - --json (raw JSON output)
960
1026
 
1027
+ ATP (Agent Transaction Protocol) subcommands:
1028
+ - buy <caps> (place an ATP order; caps is comma-separated)
1029
+ - --budget=<N> (credits to spend, default 10)
1030
+ - --question="..." (order description)
1031
+ - --routing=<mode> (fastest|cheapest|auction|swarm, default fastest)
1032
+ - --verify=<mode> (auto|ai_judge|bilateral, default auto)
1033
+ - --no-wait (return immediately after placing)
1034
+ - --timeout=<seconds> (lifecycle timeout, default 300)
1035
+ - orders (list your recent ATP orders / deliveries)
1036
+ - --role=consumer|merchant (default consumer)
1037
+ - --status=pending|verified|disputed|settled
1038
+ - --limit=<N> (1..100, default 20)
1039
+ - --json (raw JSON)
1040
+ - verify <orderId> (confirm delivery or trigger AI judge)
1041
+ - --action=confirm|ai_judge (default confirm)
1042
+
961
1043
  Validator role (decentralized validation, default ON since v1.69.0):
962
1044
  - EVOLVER_VALIDATOR_ENABLED=0 opt out (env beats persisted flag and default)
963
1045
  - 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.16",
3
+ "version": "1.69.20",
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
  };