@agent-e/core 1.6.6 → 1.6.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 +9 -2
- package/dist/index.d.ts +9 -2
- package/dist/index.js +115 -54
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +115 -54
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -321,6 +321,7 @@ interface Thresholds {
|
|
|
321
321
|
priceDiscoveryWindowTicks: number;
|
|
322
322
|
giftTradeFilterRatio: number;
|
|
323
323
|
disposalTradeWeightDiscount: number;
|
|
324
|
+
dominantRoles: string[];
|
|
324
325
|
}
|
|
325
326
|
interface TickConfig {
|
|
326
327
|
/** How many real-world units one tick represents. Default: 1 */
|
|
@@ -525,7 +526,7 @@ declare class Observer {
|
|
|
525
526
|
private tickConfig;
|
|
526
527
|
constructor(tickConfig?: Partial<TickConfig>);
|
|
527
528
|
registerCustomMetric(name: string, fn: (state: EconomyState) => number): void;
|
|
528
|
-
compute(state: EconomyState, recentEvents: EconomicEvent[]): EconomyMetrics;
|
|
529
|
+
compute(state: EconomyState, recentEvents: EconomicEvent[], personaDistribution?: Record<string, number>): EconomyMetrics;
|
|
529
530
|
}
|
|
530
531
|
|
|
531
532
|
declare class Simulator {
|
|
@@ -627,10 +628,14 @@ interface PersonaConfig {
|
|
|
627
628
|
powerUserMinSystems: number;
|
|
628
629
|
/** Rolling history window size (ticks). Default: 50 */
|
|
629
630
|
historyWindow: number;
|
|
631
|
+
/** Ticks between full reclassification. Default: 10 */
|
|
632
|
+
reclassifyInterval: number;
|
|
630
633
|
}
|
|
631
634
|
declare class PersonaTracker {
|
|
632
635
|
private agents;
|
|
633
636
|
private config;
|
|
637
|
+
private cachedDistribution;
|
|
638
|
+
private lastClassifiedTick;
|
|
634
639
|
constructor(config?: Partial<PersonaConfig>);
|
|
635
640
|
/**
|
|
636
641
|
* Ingest a state snapshot + events and update per-agent signals.
|
|
@@ -640,8 +645,10 @@ declare class PersonaTracker {
|
|
|
640
645
|
/**
|
|
641
646
|
* Classify all tracked agents and return the population distribution.
|
|
642
647
|
* Returns { Whale: 0.05, ActiveTrader: 0.18, Passive: 0.42, ... }
|
|
648
|
+
* Caches results and only reclassifies at `reclassifyInterval` boundaries.
|
|
643
649
|
*/
|
|
644
|
-
getDistribution(): Record<string, number>;
|
|
650
|
+
getDistribution(currentTick?: number): Record<string, number>;
|
|
651
|
+
private _classify;
|
|
645
652
|
}
|
|
646
653
|
|
|
647
654
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -321,6 +321,7 @@ interface Thresholds {
|
|
|
321
321
|
priceDiscoveryWindowTicks: number;
|
|
322
322
|
giftTradeFilterRatio: number;
|
|
323
323
|
disposalTradeWeightDiscount: number;
|
|
324
|
+
dominantRoles: string[];
|
|
324
325
|
}
|
|
325
326
|
interface TickConfig {
|
|
326
327
|
/** How many real-world units one tick represents. Default: 1 */
|
|
@@ -525,7 +526,7 @@ declare class Observer {
|
|
|
525
526
|
private tickConfig;
|
|
526
527
|
constructor(tickConfig?: Partial<TickConfig>);
|
|
527
528
|
registerCustomMetric(name: string, fn: (state: EconomyState) => number): void;
|
|
528
|
-
compute(state: EconomyState, recentEvents: EconomicEvent[]): EconomyMetrics;
|
|
529
|
+
compute(state: EconomyState, recentEvents: EconomicEvent[], personaDistribution?: Record<string, number>): EconomyMetrics;
|
|
529
530
|
}
|
|
530
531
|
|
|
531
532
|
declare class Simulator {
|
|
@@ -627,10 +628,14 @@ interface PersonaConfig {
|
|
|
627
628
|
powerUserMinSystems: number;
|
|
628
629
|
/** Rolling history window size (ticks). Default: 50 */
|
|
629
630
|
historyWindow: number;
|
|
631
|
+
/** Ticks between full reclassification. Default: 10 */
|
|
632
|
+
reclassifyInterval: number;
|
|
630
633
|
}
|
|
631
634
|
declare class PersonaTracker {
|
|
632
635
|
private agents;
|
|
633
636
|
private config;
|
|
637
|
+
private cachedDistribution;
|
|
638
|
+
private lastClassifiedTick;
|
|
634
639
|
constructor(config?: Partial<PersonaConfig>);
|
|
635
640
|
/**
|
|
636
641
|
* Ingest a state snapshot + events and update per-agent signals.
|
|
@@ -640,8 +645,10 @@ declare class PersonaTracker {
|
|
|
640
645
|
/**
|
|
641
646
|
* Classify all tracked agents and return the population distribution.
|
|
642
647
|
* Returns { Whale: 0.05, ActiveTrader: 0.18, Passive: 0.42, ... }
|
|
648
|
+
* Caches results and only reclassifies at `reclassifyInterval` boundaries.
|
|
643
649
|
*/
|
|
644
|
-
getDistribution(): Record<string, number>;
|
|
650
|
+
getDistribution(currentTick?: number): Record<string, number>;
|
|
651
|
+
private _classify;
|
|
645
652
|
}
|
|
646
653
|
|
|
647
654
|
/**
|
package/dist/index.js
CHANGED
|
@@ -176,7 +176,9 @@ var DEFAULT_THRESHOLDS = {
|
|
|
176
176
|
relativePriceConvergenceTarget: 0.85,
|
|
177
177
|
priceDiscoveryWindowTicks: 20,
|
|
178
178
|
giftTradeFilterRatio: 0.15,
|
|
179
|
-
disposalTradeWeightDiscount: 0.5
|
|
179
|
+
disposalTradeWeightDiscount: 0.5,
|
|
180
|
+
// Structural dominance (P8)
|
|
181
|
+
dominantRoles: []
|
|
180
182
|
};
|
|
181
183
|
var PERSONA_HEALTHY_RANGES = {
|
|
182
184
|
Active: { min: 0.2, max: 0.4 },
|
|
@@ -208,7 +210,7 @@ var Observer = class {
|
|
|
208
210
|
registerCustomMetric(name, fn) {
|
|
209
211
|
this.customMetricFns[name] = fn;
|
|
210
212
|
}
|
|
211
|
-
compute(state, recentEvents) {
|
|
213
|
+
compute(state, recentEvents, personaDistribution) {
|
|
212
214
|
if (!state.currencies || state.currencies.length === 0) {
|
|
213
215
|
console.warn("[AgentE] Warning: state.currencies is empty. Metrics will be zeroed.");
|
|
214
216
|
}
|
|
@@ -361,6 +363,16 @@ var Observer = class {
|
|
|
361
363
|
for (const [role, count] of Object.entries(populationByRole)) {
|
|
362
364
|
roleShares[role] = count / Math.max(1, totalAgents);
|
|
363
365
|
}
|
|
366
|
+
const uniqueRoles = new Set(Object.values(state.agentRoles));
|
|
367
|
+
const rolesEmpty = uniqueRoles.size <= 1;
|
|
368
|
+
if (rolesEmpty && personaDistribution && Object.keys(personaDistribution).length > 0) {
|
|
369
|
+
for (const [persona, fraction] of Object.entries(personaDistribution)) {
|
|
370
|
+
populationByRole[persona] = Math.round(fraction * totalAgents);
|
|
371
|
+
}
|
|
372
|
+
for (const [role, count] of Object.entries(populationByRole)) {
|
|
373
|
+
roleShares[role] = count / Math.max(1, totalAgents);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
364
376
|
const churnByRole = {};
|
|
365
377
|
for (const e of roleChangeEvents) {
|
|
366
378
|
const role = e.role ?? "unknown";
|
|
@@ -850,25 +862,45 @@ var P4_MaterialsFlowFasterThanCooldown = {
|
|
|
850
862
|
category: "supply_chain",
|
|
851
863
|
description: "Input delivery rate must exceed or match production cooldown rate. If producers produce every 5 ticks but only receive raw materials every 10 ticks, they starve regardless of supply levels.",
|
|
852
864
|
check(metrics, _thresholds) {
|
|
853
|
-
const { supplyByResource, populationByRole, velocity, totalAgents } = metrics;
|
|
865
|
+
const { supplyByResource, populationByRole, velocity, velocityByCurrency, totalAgents } = metrics;
|
|
854
866
|
const totalSupply = Object.values(supplyByResource).reduce((s, v) => s + v, 0);
|
|
855
867
|
const avgSupplyPerAgent = totalAgents > 0 ? totalSupply / totalAgents : 0;
|
|
856
868
|
const roleEntries = Object.entries(populationByRole);
|
|
857
869
|
const totalRoles = roleEntries.length;
|
|
858
|
-
if (totalRoles >= 2 &&
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
870
|
+
if (totalRoles >= 2 && avgSupplyPerAgent < 0.5) {
|
|
871
|
+
for (const [currency, currVelocity] of Object.entries(velocityByCurrency)) {
|
|
872
|
+
if (currVelocity < 5) {
|
|
873
|
+
return {
|
|
874
|
+
violated: true,
|
|
875
|
+
severity: 5,
|
|
876
|
+
evidence: { currency, currVelocity, avgSupplyPerAgent, totalRoles },
|
|
877
|
+
suggestedAction: {
|
|
878
|
+
parameterType: "yield",
|
|
879
|
+
scope: { currency },
|
|
880
|
+
direction: "increase",
|
|
881
|
+
magnitude: 0.15,
|
|
882
|
+
reasoning: `${currency} velocity is ${currVelocity.toFixed(1)} \u2014 materials not flowing. Increase yield to compensate.`
|
|
883
|
+
},
|
|
884
|
+
confidence: 0.7,
|
|
885
|
+
estimatedLag: 8
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
if (Object.keys(velocityByCurrency).length === 0 && velocity < 5) {
|
|
890
|
+
return {
|
|
891
|
+
violated: true,
|
|
892
|
+
severity: 5,
|
|
893
|
+
evidence: { avgSupplyPerAgent, velocity, totalRoles },
|
|
894
|
+
suggestedAction: {
|
|
895
|
+
parameterType: "yield",
|
|
896
|
+
direction: "increase",
|
|
897
|
+
magnitude: 0.15,
|
|
898
|
+
reasoning: "Low supply per agent with stagnant velocity. Increase yield to compensate."
|
|
899
|
+
},
|
|
900
|
+
confidence: 0.65,
|
|
901
|
+
estimatedLag: 8
|
|
902
|
+
};
|
|
903
|
+
}
|
|
872
904
|
}
|
|
873
905
|
if (avgSupplyPerAgent > 2) {
|
|
874
906
|
return {
|
|
@@ -1027,28 +1059,42 @@ var P8_RegulatorCannotFightDesign = {
|
|
|
1027
1059
|
id: "P8",
|
|
1028
1060
|
name: "Regulator Cannot Fight the Design",
|
|
1029
1061
|
category: "incentive",
|
|
1030
|
-
description: "If
|
|
1031
|
-
check(metrics,
|
|
1062
|
+
description: "If a role dominates above 30%, classify as structural (in dominantRoles config) or pathological. Structural dominance gets no intervention. Pathological dominance gets crowding pressure.",
|
|
1063
|
+
check(metrics, thresholds) {
|
|
1032
1064
|
const { roleShares, avgSatisfaction } = metrics;
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1065
|
+
const dominant = Object.entries(roleShares).sort((a, b) => b[1] - a[1])[0];
|
|
1066
|
+
if (!dominant) return { violated: false };
|
|
1067
|
+
const [dominantRole, dominantShare] = dominant;
|
|
1068
|
+
if (dominantShare <= 0.3) return { violated: false };
|
|
1069
|
+
const isStructural = thresholds.dominantRoles?.includes(dominantRole) ?? false;
|
|
1070
|
+
if (isStructural) {
|
|
1071
|
+
return {
|
|
1072
|
+
violated: true,
|
|
1073
|
+
severity: 3,
|
|
1074
|
+
evidence: { role: dominantRole, share: dominantShare, classification: "structural" },
|
|
1075
|
+
suggestedAction: {
|
|
1076
|
+
parameterType: "rate",
|
|
1077
|
+
direction: "set",
|
|
1078
|
+
magnitude: 0,
|
|
1079
|
+
reasoning: `${dominantRole} dominance (${(dominantShare * 100).toFixed(0)}%) is by design. No intervention.`
|
|
1080
|
+
},
|
|
1081
|
+
confidence: 0.85
|
|
1082
|
+
};
|
|
1050
1083
|
}
|
|
1051
|
-
return {
|
|
1084
|
+
return {
|
|
1085
|
+
violated: true,
|
|
1086
|
+
severity: 7,
|
|
1087
|
+
evidence: { role: dominantRole, share: dominantShare, classification: "pathological", avgSatisfaction },
|
|
1088
|
+
suggestedAction: {
|
|
1089
|
+
parameterType: "reward",
|
|
1090
|
+
scope: { tags: [dominantRole] },
|
|
1091
|
+
direction: "decrease",
|
|
1092
|
+
magnitude: 0.1,
|
|
1093
|
+
reasoning: `${dominantRole} at ${(dominantShare * 100).toFixed(0)}% is not a designed majority \u2014 apply crowding pressure.`
|
|
1094
|
+
},
|
|
1095
|
+
confidence: 0.7,
|
|
1096
|
+
estimatedLag: 12
|
|
1097
|
+
};
|
|
1052
1098
|
}
|
|
1053
1099
|
};
|
|
1054
1100
|
var INCENTIVE_PRINCIPLES = [
|
|
@@ -1314,7 +1360,7 @@ var P15_PoolsNeedCapAndDecay = {
|
|
|
1314
1360
|
id: "P15",
|
|
1315
1361
|
name: "Pools Need Cap + Decay",
|
|
1316
1362
|
category: "currency",
|
|
1317
|
-
description: "Any pool (bank, reward pool) without a cap accumulates infinitely. A pool at 42% of total supply means 42% of the economy is frozen. Cap
|
|
1363
|
+
description: "Any pool (bank, reward pool) without a cap accumulates infinitely. A pool at 42% of total supply means 42% of the economy is frozen. Cap configurable (default 10%). Violation fires at 2\xD7 cap share of total supply.",
|
|
1318
1364
|
check(metrics, thresholds) {
|
|
1319
1365
|
const { poolCapPercent } = thresholds;
|
|
1320
1366
|
for (const [pool, currencyAmounts] of Object.entries(metrics.poolSizesByCurrency)) {
|
|
@@ -1347,7 +1393,7 @@ var P16_WithdrawalPenaltyScales = {
|
|
|
1347
1393
|
id: "P16",
|
|
1348
1394
|
name: "Withdrawal Penalty Scales with Lock Duration",
|
|
1349
1395
|
category: "currency",
|
|
1350
|
-
description: "
|
|
1396
|
+
description: "Depleted pool with estimated high staking signals early-withdrawal abuse. Symptom detector \u2014 penalty formula is developer responsibility.",
|
|
1351
1397
|
check(metrics, _thresholds) {
|
|
1352
1398
|
for (const [poolName, currencyAmounts] of Object.entries(metrics.poolSizesByCurrency)) {
|
|
1353
1399
|
for (const curr of metrics.currencies) {
|
|
@@ -1463,7 +1509,7 @@ var P17_GracePeriodBeforeIntervention = {
|
|
|
1463
1509
|
id: "P17",
|
|
1464
1510
|
name: "Grace Period Before Intervention",
|
|
1465
1511
|
category: "bootstrap",
|
|
1466
|
-
description: "Any intervention before tick
|
|
1512
|
+
description: "Any intervention before tick 30 is premature. The economy needs time to bootstrap with designed distributions. Early intervention against designed dominance can kill the economy instantly.",
|
|
1467
1513
|
check(metrics, _thresholds) {
|
|
1468
1514
|
if (metrics.tick < 30 && metrics.avgSatisfaction < 40) {
|
|
1469
1515
|
return {
|
|
@@ -1784,7 +1830,7 @@ var P27_AdjustmentsNeedCooldowns = {
|
|
|
1784
1830
|
id: "P27",
|
|
1785
1831
|
name: "Adjustments Need Cooldowns",
|
|
1786
1832
|
category: "regulator",
|
|
1787
|
-
description: "
|
|
1833
|
+
description: "High churn + low satisfaction may indicate oscillation from rapid adjustments. Cooldown enforcement is structural (Planner). This is a symptom detector.",
|
|
1788
1834
|
check(metrics, _thresholds) {
|
|
1789
1835
|
const { churnRate, avgSatisfaction } = metrics;
|
|
1790
1836
|
if (churnRate > 0.08 && avgSatisfaction < 50) {
|
|
@@ -1841,7 +1887,7 @@ var P38_CommunicationPreventsRevolt = {
|
|
|
1841
1887
|
id: "P38",
|
|
1842
1888
|
name: "Communication Prevents Revolt",
|
|
1843
1889
|
category: "regulator",
|
|
1844
|
-
description: "
|
|
1890
|
+
description: "High churn may indicate unexplained changes. Logging enforcement is structural (DecisionLog). Flags high churn as signal to review recent decisions.",
|
|
1845
1891
|
check(metrics, _thresholds) {
|
|
1846
1892
|
const { churnRate } = metrics;
|
|
1847
1893
|
if (churnRate > 0.1) {
|
|
@@ -1866,7 +1912,7 @@ var REGULATOR_PRINCIPLES = [
|
|
|
1866
1912
|
P25_CorrectLeversForCorrectProblems,
|
|
1867
1913
|
P26_ContinuousPressureBeatsThresholdCuts,
|
|
1868
1914
|
P27_AdjustmentsNeedCooldowns,
|
|
1869
|
-
|
|
1915
|
+
// P28 merged into P8 (v1.6.7)
|
|
1870
1916
|
P38_CommunicationPreventsRevolt
|
|
1871
1917
|
];
|
|
1872
1918
|
|
|
@@ -2175,7 +2221,7 @@ var P43_SimulationMinimum = {
|
|
|
2175
2221
|
id: "P43",
|
|
2176
2222
|
name: "Simulation Minimum (100 Iterations)",
|
|
2177
2223
|
category: "statistical",
|
|
2178
|
-
description: "
|
|
2224
|
+
description: "Wild inflation swings (>30%) may indicate insufficient simulation data. Minimum iteration enforcement is structural (Simulator config). Symptom detector.",
|
|
2179
2225
|
check(metrics, thresholds) {
|
|
2180
2226
|
const { inflationRate } = metrics;
|
|
2181
2227
|
if (Math.abs(inflationRate) > 0.3) {
|
|
@@ -2342,7 +2388,7 @@ var P49_IdleAssetTax = {
|
|
|
2342
2388
|
id: "P49",
|
|
2343
2389
|
name: "Idle Asset Tax",
|
|
2344
2390
|
category: "resource",
|
|
2345
|
-
description:
|
|
2391
|
+
description: "Concentrated idle wealth (Gini >0.55, top 10% >60%, velocity <5). Raises transaction fees as proxy holding tax. Decay/storage/expiry requires developer implementation.",
|
|
2346
2392
|
check(metrics, _thresholds) {
|
|
2347
2393
|
const { giniCoefficient, top10PctShare, velocity } = metrics;
|
|
2348
2394
|
if (giniCoefficient > 0.55 && top10PctShare > 0.6 && velocity < 5) {
|
|
@@ -2375,7 +2421,7 @@ var P33_FairNotEqual = {
|
|
|
2375
2421
|
id: "P33",
|
|
2376
2422
|
name: "Fair \u2260 Equal",
|
|
2377
2423
|
category: "participant_experience",
|
|
2378
|
-
description: "Gini = 0 is boring \u2014 everyone has the same and there is nothing to strive for. Healthy inequality from skill/effort is fine. Inequality from money (pay-to-win) is toxic.
|
|
2424
|
+
description: "Gini = 0 is boring \u2014 everyone has the same and there is nothing to strive for. Healthy inequality from skill/effort is fine. Inequality from money (pay-to-win) is toxic. Below 0.10 Gini = too flat; above configurable thresholds = oligarchy.",
|
|
2379
2425
|
check(metrics, thresholds) {
|
|
2380
2426
|
for (const curr of metrics.currencies) {
|
|
2381
2427
|
const giniCoefficient = metrics.giniCoefficientByCurrency[curr] ?? 0;
|
|
@@ -2485,7 +2531,7 @@ var P45_TimeBudget = {
|
|
|
2485
2531
|
id: "P45",
|
|
2486
2532
|
name: "Time Budget",
|
|
2487
2533
|
category: "participant_experience",
|
|
2488
|
-
description: "
|
|
2534
|
+
description: "timeToValue > 30 with satisfaction < 55 suggests excessive time demand. Proxy metric \u2014 does not measure individual available time.",
|
|
2489
2535
|
check(metrics, thresholds) {
|
|
2490
2536
|
const { timeToValue, avgSatisfaction } = metrics;
|
|
2491
2537
|
const timePressure = timeToValue > 30;
|
|
@@ -2723,7 +2769,7 @@ var P52_EndowmentEffect = {
|
|
|
2723
2769
|
id: "P52",
|
|
2724
2770
|
name: "Endowment Effect",
|
|
2725
2771
|
category: "operations",
|
|
2726
|
-
description: "
|
|
2772
|
+
description: "High completion (>90%) + low satisfaction (<60) suggests activities not creating perceived value. May indicate missing endowment effect.",
|
|
2727
2773
|
check(metrics, _thresholds) {
|
|
2728
2774
|
const { avgSatisfaction, churnRate } = metrics;
|
|
2729
2775
|
const { eventCompletionRate } = metrics;
|
|
@@ -2796,7 +2842,7 @@ var P54_OperationalCadence = {
|
|
|
2796
2842
|
id: "P54",
|
|
2797
2843
|
name: "Operational Cadence",
|
|
2798
2844
|
category: "operations",
|
|
2799
|
-
description: "
|
|
2845
|
+
description: "Low velocity (<2) + low satisfaction after tick 100 = supply stagnation. Advisory signal for developer to audit content freshness.",
|
|
2800
2846
|
check(metrics, _thresholds) {
|
|
2801
2847
|
const { velocity, avgSatisfaction } = metrics;
|
|
2802
2848
|
if (velocity < 2 && avgSatisfaction < 55 && metrics.tick > 100) {
|
|
@@ -3646,11 +3692,14 @@ var DEFAULT_PERSONA_CONFIG = {
|
|
|
3646
3692
|
dormantWindow: 20,
|
|
3647
3693
|
atRiskDropThreshold: 0.5,
|
|
3648
3694
|
powerUserMinSystems: 3,
|
|
3649
|
-
historyWindow: 50
|
|
3695
|
+
historyWindow: 50,
|
|
3696
|
+
reclassifyInterval: 10
|
|
3650
3697
|
};
|
|
3651
3698
|
var PersonaTracker = class {
|
|
3652
3699
|
constructor(config) {
|
|
3653
3700
|
this.agents = /* @__PURE__ */ new Map();
|
|
3701
|
+
this.cachedDistribution = {};
|
|
3702
|
+
this.lastClassifiedTick = -Infinity;
|
|
3654
3703
|
this.config = { ...DEFAULT_PERSONA_CONFIG, ...config };
|
|
3655
3704
|
}
|
|
3656
3705
|
/**
|
|
@@ -3658,6 +3707,7 @@ var PersonaTracker = class {
|
|
|
3658
3707
|
* Call this once per tick BEFORE getDistribution().
|
|
3659
3708
|
*/
|
|
3660
3709
|
update(state, events) {
|
|
3710
|
+
if (!state.agentBalances) return;
|
|
3661
3711
|
const tick = state.tick;
|
|
3662
3712
|
const txByAgent = /* @__PURE__ */ new Map();
|
|
3663
3713
|
if (events) {
|
|
@@ -3713,8 +3763,18 @@ var PersonaTracker = class {
|
|
|
3713
3763
|
/**
|
|
3714
3764
|
* Classify all tracked agents and return the population distribution.
|
|
3715
3765
|
* Returns { Whale: 0.05, ActiveTrader: 0.18, Passive: 0.42, ... }
|
|
3766
|
+
* Caches results and only reclassifies at `reclassifyInterval` boundaries.
|
|
3716
3767
|
*/
|
|
3717
|
-
getDistribution() {
|
|
3768
|
+
getDistribution(currentTick) {
|
|
3769
|
+
const tick = currentTick ?? 0;
|
|
3770
|
+
if (tick - this.lastClassifiedTick < this.config.reclassifyInterval && Object.keys(this.cachedDistribution).length > 0) {
|
|
3771
|
+
return this.cachedDistribution;
|
|
3772
|
+
}
|
|
3773
|
+
this.lastClassifiedTick = tick;
|
|
3774
|
+
this.cachedDistribution = this._classify();
|
|
3775
|
+
return this.cachedDistribution;
|
|
3776
|
+
}
|
|
3777
|
+
_classify() {
|
|
3718
3778
|
const agentIds = [...this.agents.keys()];
|
|
3719
3779
|
const total = agentIds.length;
|
|
3720
3780
|
if (total === 0) return {};
|
|
@@ -4020,16 +4080,17 @@ var AgentE = class {
|
|
|
4020
4080
|
this.currentTick = currentState.tick;
|
|
4021
4081
|
const events = this.eventBuffer;
|
|
4022
4082
|
this.eventBuffer = [];
|
|
4083
|
+
this.personaTracker.update(currentState, events);
|
|
4084
|
+
const personaDist = this.personaTracker.getDistribution(currentState.tick);
|
|
4023
4085
|
let metrics;
|
|
4024
4086
|
try {
|
|
4025
|
-
metrics = this.observer.compute(currentState, events);
|
|
4087
|
+
metrics = this.observer.compute(currentState, events, personaDist);
|
|
4026
4088
|
} catch (err) {
|
|
4027
4089
|
console.error(`[AgentE] Observer.compute() failed at tick ${currentState.tick}:`, err);
|
|
4028
4090
|
return;
|
|
4029
4091
|
}
|
|
4030
4092
|
this.store.record(metrics);
|
|
4031
|
-
|
|
4032
|
-
metrics.personaDistribution = this.personaTracker.getDistribution();
|
|
4093
|
+
metrics.personaDistribution = personaDist;
|
|
4033
4094
|
const { rolledBack, settled } = await this.executor.checkRollbacks(metrics, this.adapter);
|
|
4034
4095
|
for (const plan2 of rolledBack) {
|
|
4035
4096
|
this.planner.recordRolledBack(plan2);
|