@agent-e/core 1.5.5 → 1.5.7
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/dist/index.d.mts +30 -8
- package/dist/index.d.ts +30 -8
- package/dist/index.js +130 -64
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +130 -64
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -3484,86 +3484,152 @@ var MetricStore = class {
|
|
|
3484
3484
|
};
|
|
3485
3485
|
|
|
3486
3486
|
// src/PersonaTracker.ts
|
|
3487
|
+
var DEFAULT_PERSONA_CONFIG = {
|
|
3488
|
+
whalePercentile: 0.05,
|
|
3489
|
+
activeTraderPercentile: 0.2,
|
|
3490
|
+
newEntrantWindow: 10,
|
|
3491
|
+
dormantWindow: 20,
|
|
3492
|
+
atRiskDropThreshold: 0.5,
|
|
3493
|
+
powerUserMinSystems: 3,
|
|
3494
|
+
historyWindow: 50
|
|
3495
|
+
};
|
|
3487
3496
|
var PersonaTracker = class {
|
|
3488
|
-
constructor() {
|
|
3489
|
-
this.
|
|
3490
|
-
this.
|
|
3497
|
+
constructor(config) {
|
|
3498
|
+
this.agents = /* @__PURE__ */ new Map();
|
|
3499
|
+
this.config = { ...DEFAULT_PERSONA_CONFIG, ...config };
|
|
3491
3500
|
}
|
|
3492
|
-
/**
|
|
3493
|
-
|
|
3501
|
+
/**
|
|
3502
|
+
* Ingest a state snapshot + events and update per-agent signals.
|
|
3503
|
+
* Call this once per tick BEFORE getDistribution().
|
|
3504
|
+
*/
|
|
3505
|
+
update(state, events) {
|
|
3494
3506
|
const tick = state.tick;
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
const
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3507
|
+
const txByAgent = /* @__PURE__ */ new Map();
|
|
3508
|
+
if (events) {
|
|
3509
|
+
for (const e of events) {
|
|
3510
|
+
const agents = [e.actor];
|
|
3511
|
+
if (e.from) agents.push(e.from);
|
|
3512
|
+
if (e.to) agents.push(e.to);
|
|
3513
|
+
for (const id of agents) {
|
|
3514
|
+
if (!id) continue;
|
|
3515
|
+
const entry = txByAgent.get(id) ?? { count: 0, volume: 0, systems: /* @__PURE__ */ new Set() };
|
|
3516
|
+
entry.count++;
|
|
3517
|
+
entry.volume += e.amount ?? 0;
|
|
3518
|
+
if (e.system) entry.systems.add(e.system);
|
|
3519
|
+
txByAgent.set(id, entry);
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3523
|
+
for (const [agentId, balances] of Object.entries(state.agentBalances)) {
|
|
3524
|
+
const totalHoldings = Object.values(balances).reduce((s, v) => s + v, 0);
|
|
3525
|
+
const tx = txByAgent.get(agentId);
|
|
3526
|
+
const record = this.agents.get(agentId) ?? {
|
|
3527
|
+
firstSeen: tick,
|
|
3528
|
+
lastActive: tick,
|
|
3529
|
+
snapshots: [],
|
|
3530
|
+
previousTxRate: 0
|
|
3531
|
+
};
|
|
3532
|
+
const snapshot = {
|
|
3533
|
+
totalHoldings,
|
|
3534
|
+
txCount: tx?.count ?? 0,
|
|
3535
|
+
txVolume: tx?.volume ?? 0,
|
|
3536
|
+
systems: tx?.systems ?? /* @__PURE__ */ new Set()
|
|
3537
|
+
};
|
|
3538
|
+
record.snapshots.push(snapshot);
|
|
3539
|
+
if (record.snapshots.length > this.config.historyWindow) {
|
|
3540
|
+
const oldHalf = record.snapshots.slice(0, Math.floor(record.snapshots.length / 2));
|
|
3541
|
+
record.previousTxRate = oldHalf.reduce((s, sn) => s + sn.txCount, 0) / Math.max(1, oldHalf.length);
|
|
3542
|
+
record.snapshots = record.snapshots.slice(-this.config.historyWindow);
|
|
3543
|
+
}
|
|
3544
|
+
if ((tx?.count ?? 0) > 0) {
|
|
3545
|
+
record.lastActive = tick;
|
|
3546
|
+
}
|
|
3547
|
+
this.agents.set(agentId, record);
|
|
3548
|
+
}
|
|
3549
|
+
const pruneThreshold = this.config.dormantWindow * 2;
|
|
3550
|
+
if (tick % this.config.dormantWindow === 0) {
|
|
3551
|
+
for (const [id, rec] of this.agents) {
|
|
3552
|
+
if (tick - rec.lastActive > pruneThreshold && !(id in state.agentBalances)) {
|
|
3553
|
+
this.agents.delete(id);
|
|
3519
3554
|
}
|
|
3520
3555
|
}
|
|
3521
3556
|
}
|
|
3522
3557
|
}
|
|
3523
|
-
/**
|
|
3558
|
+
/**
|
|
3559
|
+
* Classify all tracked agents and return the population distribution.
|
|
3560
|
+
* Returns { Whale: 0.05, ActiveTrader: 0.18, Passive: 0.42, ... }
|
|
3561
|
+
*/
|
|
3524
3562
|
getDistribution() {
|
|
3525
|
-
const
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3563
|
+
const agentIds = [...this.agents.keys()];
|
|
3564
|
+
const total = agentIds.length;
|
|
3565
|
+
if (total === 0) return {};
|
|
3566
|
+
const holdings = agentIds.map((id) => {
|
|
3567
|
+
const rec = this.agents.get(id);
|
|
3568
|
+
const latest = rec.snapshots[rec.snapshots.length - 1];
|
|
3569
|
+
return latest?.totalHoldings ?? 0;
|
|
3570
|
+
}).sort((a, b) => a - b);
|
|
3571
|
+
const whaleThreshold = percentile(holdings, 1 - this.config.whalePercentile);
|
|
3572
|
+
const txRates = agentIds.map((id) => {
|
|
3573
|
+
const rec = this.agents.get(id);
|
|
3574
|
+
return rec.snapshots.reduce((s, sn) => s + sn.txCount, 0) / Math.max(1, rec.snapshots.length);
|
|
3575
|
+
}).sort((a, b) => a - b);
|
|
3576
|
+
const activeTraderThreshold = percentile(txRates, 1 - this.config.activeTraderPercentile);
|
|
3577
|
+
const medianTxRate = percentile(txRates, 0.5);
|
|
3578
|
+
const counts = {};
|
|
3579
|
+
const currentTick = Math.max(...agentIds.map((id) => this.agents.get(id).lastActive), 0);
|
|
3580
|
+
for (let i = 0; i < agentIds.length; i++) {
|
|
3581
|
+
const id = agentIds[i];
|
|
3582
|
+
const rec = this.agents.get(id);
|
|
3583
|
+
const snaps = rec.snapshots;
|
|
3584
|
+
if (snaps.length === 0) continue;
|
|
3585
|
+
const latestHoldings = snaps[snaps.length - 1].totalHoldings;
|
|
3586
|
+
const agentTxRate = txRates[i];
|
|
3587
|
+
const ticksSinceFirst = currentTick - rec.firstSeen;
|
|
3588
|
+
const ticksSinceActive = currentTick - rec.lastActive;
|
|
3589
|
+
const halfIdx = Math.floor(snaps.length / 2);
|
|
3590
|
+
const earlyAvg = snaps.slice(0, Math.max(1, halfIdx)).reduce((s, sn) => s + sn.totalHoldings, 0) / Math.max(1, halfIdx);
|
|
3591
|
+
const lateAvg = snaps.slice(halfIdx).reduce((s, sn) => s + sn.totalHoldings, 0) / Math.max(1, snaps.length - halfIdx);
|
|
3592
|
+
const balanceDelta = lateAvg - earlyAvg;
|
|
3593
|
+
const allSystems = /* @__PURE__ */ new Set();
|
|
3594
|
+
for (const sn of snaps) {
|
|
3595
|
+
for (const sys of sn.systems) allSystems.add(sys);
|
|
3596
|
+
}
|
|
3597
|
+
const recentSnaps = snaps.slice(-Math.min(10, snaps.length));
|
|
3598
|
+
const recentTxRate = recentSnaps.reduce((s, sn) => s + sn.txCount, 0) / Math.max(1, recentSnaps.length);
|
|
3599
|
+
let persona;
|
|
3600
|
+
if (ticksSinceFirst <= this.config.newEntrantWindow) {
|
|
3601
|
+
persona = "NewEntrant";
|
|
3602
|
+
} else if (latestHoldings > 0 && ticksSinceActive > this.config.dormantWindow) {
|
|
3603
|
+
persona = "Dormant";
|
|
3604
|
+
} else if (rec.previousTxRate > 0 && recentTxRate < rec.previousTxRate * (1 - this.config.atRiskDropThreshold)) {
|
|
3605
|
+
persona = "AtRisk";
|
|
3606
|
+
} else if (latestHoldings >= whaleThreshold && whaleThreshold > 0) {
|
|
3607
|
+
persona = "Whale";
|
|
3608
|
+
} else if (allSystems.size >= this.config.powerUserMinSystems) {
|
|
3609
|
+
persona = "PowerUser";
|
|
3610
|
+
} else if (agentTxRate >= activeTraderThreshold && activeTraderThreshold > 0) {
|
|
3611
|
+
persona = "ActiveTrader";
|
|
3612
|
+
} else if (balanceDelta > 0 && agentTxRate < medianTxRate) {
|
|
3613
|
+
persona = "Accumulator";
|
|
3614
|
+
} else if (balanceDelta < 0 && agentTxRate >= medianTxRate) {
|
|
3615
|
+
persona = "Spender";
|
|
3616
|
+
} else {
|
|
3617
|
+
persona = "Passive";
|
|
3618
|
+
}
|
|
3539
3619
|
counts[persona] = (counts[persona] ?? 0) + 1;
|
|
3540
|
-
total++;
|
|
3541
3620
|
}
|
|
3542
|
-
if (total === 0) return {};
|
|
3543
3621
|
const distribution = {};
|
|
3544
3622
|
for (const [persona, count] of Object.entries(counts)) {
|
|
3545
3623
|
distribution[persona] = count / total;
|
|
3546
3624
|
}
|
|
3547
3625
|
return distribution;
|
|
3548
3626
|
}
|
|
3549
|
-
classify(history) {
|
|
3550
|
-
if (history.length === 0) return "Active";
|
|
3551
|
-
const avg = (key) => {
|
|
3552
|
-
const vals = history.map((h) => h[key]);
|
|
3553
|
-
return vals.reduce((s, v) => s + v, 0) / vals.length;
|
|
3554
|
-
};
|
|
3555
|
-
const txRate = avg("transactionCount");
|
|
3556
|
-
const extraction = avg("netExtraction");
|
|
3557
|
-
const uniqueItems = avg("uniqueItemsHeld");
|
|
3558
|
-
const spend = avg("spendAmount");
|
|
3559
|
-
if (spend > 1e3) return "HighValue";
|
|
3560
|
-
if (txRate > 10) return "Trader";
|
|
3561
|
-
if (uniqueItems > 5 && extraction < 0) return "Collector";
|
|
3562
|
-
if (extraction > 100) return "Earner";
|
|
3563
|
-
if (extraction > 50) return "Speculator";
|
|
3564
|
-
return "Active";
|
|
3565
|
-
}
|
|
3566
3627
|
};
|
|
3628
|
+
function percentile(sorted, p) {
|
|
3629
|
+
if (sorted.length === 0) return 0;
|
|
3630
|
+
const idx = Math.ceil(p * sorted.length) - 1;
|
|
3631
|
+
return sorted[Math.max(0, Math.min(idx, sorted.length - 1))];
|
|
3632
|
+
}
|
|
3567
3633
|
|
|
3568
3634
|
// src/ParameterRegistry.ts
|
|
3569
3635
|
var ParameterRegistry = class {
|
|
@@ -3806,7 +3872,7 @@ var AgentE = class {
|
|
|
3806
3872
|
return;
|
|
3807
3873
|
}
|
|
3808
3874
|
this.store.record(metrics);
|
|
3809
|
-
this.personaTracker.update(currentState);
|
|
3875
|
+
this.personaTracker.update(currentState, events);
|
|
3810
3876
|
metrics.personaDistribution = this.personaTracker.getDistribution();
|
|
3811
3877
|
const { rolledBack, settled } = await this.executor.checkRollbacks(metrics, this.adapter);
|
|
3812
3878
|
for (const plan2 of rolledBack) {
|