@agent-e/core 1.5.1 → 1.5.3

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.js CHANGED
@@ -90,7 +90,7 @@ __export(index_exports, {
90
90
  P57_CombinatorialPriceSpace: () => P57_CombinatorialPriceSpace,
91
91
  P58_NoNaturalNumeraire: () => P58_NoNaturalNumeraire,
92
92
  P59_GiftEconomyNoise: () => P59_GiftEconomyNoise,
93
- P5_ProfitabilityIsCompetitive: () => P5_ProfitabilityIsCompetitive,
93
+ P5_ProfitabilityIsRelative: () => P5_ProfitabilityIsRelative,
94
94
  P60_SurplusDisposalAsymmetry: () => P60_SurplusDisposalAsymmetry,
95
95
  P6_CrowdingMultiplierOnAllRoles: () => P6_CrowdingMultiplierOnAllRoles,
96
96
  P7_NonSpecialistsSubsidiseSpecialists: () => P7_NonSpecialistsSubsidiseSpecialists,
@@ -148,7 +148,7 @@ var DEFAULT_THRESHOLDS = {
148
148
  cooldownTicks: 15,
149
149
  // Currency (P13)
150
150
  poolWinRate: 0.65,
151
- poolHouseCut: 0.1,
151
+ poolOperatorShare: 0.1,
152
152
  // Population balance (P9)
153
153
  roleSwitchFrictionMax: 0.05,
154
154
  // >5% of population switching in one period = herd
@@ -209,6 +209,12 @@ var Observer = class {
209
209
  this.customMetricFns[name] = fn;
210
210
  }
211
211
  compute(state, recentEvents) {
212
+ if (!state.currencies || state.currencies.length === 0) {
213
+ console.warn("[AgentE] Warning: state.currencies is empty. Metrics will be zeroed.");
214
+ }
215
+ if (!state.agentBalances || Object.keys(state.agentBalances).length === 0) {
216
+ console.warn("[AgentE] Warning: state.agentBalances is empty.");
217
+ }
212
218
  const tick = state.tick;
213
219
  const roles = Object.values(state.agentRoles);
214
220
  const totalAgents = Object.keys(state.agentBalances).length;
@@ -303,7 +309,7 @@ var Observer = class {
303
309
  const faucet = faucetVolumeByCurrency[curr] ?? 0;
304
310
  const sink = sinkVolumeByCurrency[curr] ?? 0;
305
311
  netFlowByCurrency[curr] = faucet - sink;
306
- tapSinkRatioByCurrency[curr] = sink > 0 ? faucet / sink : faucet > 0 ? Infinity : 1;
312
+ tapSinkRatioByCurrency[curr] = sink > 0 ? Math.min(faucet / sink, 100) : faucet > 0 ? 100 : 1;
307
313
  const prevSupply = this.previousMetrics?.totalSupplyByCurrency?.[curr] ?? totalSupplyByCurrency[curr] ?? 0;
308
314
  const currSupply = totalSupplyByCurrency[curr] ?? 0;
309
315
  inflationRateByCurrency[curr] = prevSupply > 0 ? (currSupply - prevSupply) / prevSupply : 0;
@@ -338,7 +344,7 @@ var Observer = class {
338
344
  const faucetVolume = Object.values(faucetVolumeByCurrency).reduce((s, v) => s + v, 0);
339
345
  const sinkVolume = Object.values(sinkVolumeByCurrency).reduce((s, v) => s + v, 0);
340
346
  const netFlow = faucetVolume - sinkVolume;
341
- const tapSinkRatio = sinkVolume > 0 ? faucetVolume / sinkVolume : faucetVolume > 0 ? Infinity : 1;
347
+ const tapSinkRatio = sinkVolume > 0 ? Math.min(faucetVolume / sinkVolume, 100) : faucetVolume > 0 ? 100 : 1;
342
348
  const velocity = totalSupply > 0 ? tradeEvents.length / totalSupply : 0;
343
349
  const prevTotalSupply = this.previousMetrics?.totalSupply ?? totalSupply;
344
350
  const inflationRate = prevTotalSupply > 0 ? (totalSupply - prevTotalSupply) / prevTotalSupply : 0;
@@ -376,7 +382,9 @@ var Observer = class {
376
382
  const pVals = Object.values(resourcePrices);
377
383
  priceIndexByCurrency[curr] = pVals.length > 0 ? pVals.reduce((s, p) => s + p, 0) / pVals.length : 0;
378
384
  }
379
- this.previousPricesByCurrency = JSON.parse(JSON.stringify(pricesByCurrency));
385
+ this.previousPricesByCurrency = Object.fromEntries(
386
+ Object.entries(pricesByCurrency).map(([c, p]) => [c, { ...p }])
387
+ );
380
388
  const prices = pricesByCurrency[defaultCurrency] ?? {};
381
389
  const priceVolatility = priceVolatilityByCurrency[defaultCurrency] ?? {};
382
390
  const priceIndex = priceIndexByCurrency[defaultCurrency] ?? 0;
@@ -396,9 +404,9 @@ var Observer = class {
396
404
  for (const resource of /* @__PURE__ */ new Set([...Object.keys(supplyByResource), ...Object.keys(demandSignals)])) {
397
405
  const s = supplyByResource[resource] ?? 0;
398
406
  const d = demandSignals[resource] ?? 0;
399
- if (d > 2 && s / d < 0.5) {
407
+ if (d > 0 && d > 2 && s / d < 0.5) {
400
408
  pinchPoints[resource] = "scarce";
401
- } else if (s > 3 && d > 0 && s / d > 3) {
409
+ } else if (d > 0 && s > 3 && s / d > 3) {
402
410
  pinchPoints[resource] = "oversupplied";
403
411
  } else {
404
412
  pinchPoints[resource] = "optimal";
@@ -444,21 +452,11 @@ var Observer = class {
444
452
  const arbitrageIndexByCurrency = {};
445
453
  for (const curr of currencies) {
446
454
  const cPrices = pricesByCurrency[curr] ?? {};
447
- const priceKeys = Object.keys(cPrices).filter((k) => cPrices[k] > 0);
448
- if (priceKeys.length >= 2) {
449
- let pairCount = 0;
450
- let totalDivergence = 0;
451
- for (let i = 0; i < priceKeys.length; i++) {
452
- for (let j = i + 1; j < priceKeys.length; j++) {
453
- const pA = cPrices[priceKeys[i]];
454
- const pB = cPrices[priceKeys[j]];
455
- let ratio = pA / pB;
456
- ratio = Math.max(1e-3, Math.min(1e3, ratio));
457
- totalDivergence += Math.abs(Math.log(ratio));
458
- pairCount++;
459
- }
460
- }
461
- arbitrageIndexByCurrency[curr] = pairCount > 0 ? Math.min(1, totalDivergence / pairCount) : 0;
455
+ const logPrices = Object.values(cPrices).filter((p) => p > 0).map((p) => Math.log(p));
456
+ if (logPrices.length >= 2) {
457
+ const mean = logPrices.reduce((s, v) => s + v, 0) / logPrices.length;
458
+ const variance = logPrices.reduce((s, v) => s + (v - mean) ** 2, 0) / logPrices.length;
459
+ arbitrageIndexByCurrency[curr] = Math.min(1, Math.sqrt(variance));
462
460
  } else {
463
461
  arbitrageIndexByCurrency[curr] = 0;
464
462
  }
@@ -545,10 +543,10 @@ var Observer = class {
545
543
  prices,
546
544
  priceVolatility,
547
545
  poolSizes: poolSizesAggregate,
548
- extractionRatio: NaN,
549
- newUserDependency: NaN,
550
- smokeTestRatio: NaN,
551
- currencyInsulation: NaN,
546
+ extractionRatio: 0,
547
+ newUserDependency: 0,
548
+ smokeTestRatio: 0,
549
+ currencyInsulation: 0,
552
550
  arbitrageIndex,
553
551
  giftTradeRatio,
554
552
  disposalTradeRatio,
@@ -570,7 +568,7 @@ var Observer = class {
570
568
  timeToValue,
571
569
  sharkToothPeaks: this.previousMetrics?.sharkToothPeaks ?? [],
572
570
  sharkToothValleys: this.previousMetrics?.sharkToothValleys ?? [],
573
- eventCompletionRate: NaN,
571
+ eventCompletionRate: 0,
574
572
  contentDropAge,
575
573
  systems: state.systems ?? [],
576
574
  sources: state.sources ?? [],
@@ -602,7 +600,7 @@ function computeGini(sorted) {
602
600
  for (let i = 0; i < n; i++) {
603
601
  numerator += (2 * (i + 1) - n - 1) * (sorted[i] ?? 0);
604
602
  }
605
- return Math.abs(numerator) / (n * sum);
603
+ return Math.min(1, Math.abs(numerator) / (n * sum));
606
604
  }
607
605
 
608
606
  // src/Diagnoser.ts
@@ -698,10 +696,10 @@ function emptyMetrics(tick = 0) {
698
696
  prices: {},
699
697
  priceVolatility: {},
700
698
  poolSizes: {},
701
- extractionRatio: NaN,
702
- newUserDependency: NaN,
703
- smokeTestRatio: NaN,
704
- currencyInsulation: NaN,
699
+ extractionRatio: 0,
700
+ newUserDependency: 0,
701
+ smokeTestRatio: 0,
702
+ currencyInsulation: 0,
705
703
  arbitrageIndex: 0,
706
704
  giftTradeRatio: 0,
707
705
  disposalTradeRatio: 0,
@@ -722,7 +720,7 @@ function emptyMetrics(tick = 0) {
722
720
  timeToValue: 0,
723
721
  sharkToothPeaks: [],
724
722
  sharkToothValleys: [],
725
- eventCompletionRate: NaN,
723
+ eventCompletionRate: 0,
726
724
  contentDropAge: 0,
727
725
  systems: [],
728
726
  sources: [],
@@ -927,9 +925,9 @@ var SUPPLY_CHAIN_PRINCIPLES = [
927
925
  ];
928
926
 
929
927
  // src/principles/incentives.ts
930
- var P5_ProfitabilityIsCompetitive = {
928
+ var P5_ProfitabilityIsRelative = {
931
929
  id: "P5",
932
- name: "Profitability Is Competitive, Not Absolute",
930
+ name: "Profitability Is Relative, Not Absolute",
933
931
  category: "incentive",
934
932
  description: "Any profitability formula that returns the same number regardless of how many agents are already in that role will cause stampedes. 97 intermediaries happened because profit = transactions \xD7 10 with no competition denominator.",
935
933
  check(metrics, thresholds) {
@@ -993,7 +991,7 @@ var P7_NonSpecialistsSubsidiseSpecialists = {
993
991
  id: "P7",
994
992
  name: "Non-Specialists Subsidise Specialists in Zero-Sum Games",
995
993
  category: "incentive",
996
- description: "In zero-sum pools (competitive pool, staking), the math only works if non-specialists overpay relative to specialists. If the pool is >70% specialists, there is no one left to subsidise and the pot drains.",
994
+ description: "In zero-sum pools (staking, prize pools, etc.), the math only works if non-specialists overpay relative to specialists. If the pool is >70% specialists, there is no one left to subsidise and the pot drains.",
997
995
  check(metrics, _thresholds) {
998
996
  const { poolSizes } = metrics;
999
997
  for (const [poolName, poolSize] of Object.entries(poolSizes)) {
@@ -1029,7 +1027,7 @@ var P8_RegulatorCannotFightDesign = {
1029
1027
  id: "P8",
1030
1028
  name: "Regulator Cannot Fight the Design",
1031
1029
  category: "incentive",
1032
- description: "If the economy is designed to have a majority role (e.g. dominant role exceeds 55%), the regulator must know this and exempt that role from population suppression. AgentE at tick 1 seeing dominant role exceeds 55% and slashing competitive pool rewards is overreach.",
1030
+ description: "If the economy is designed to have a majority role (e.g. dominant role exceeds 55%), the regulator must know this and exempt that role from population suppression. AgentE at tick 1 seeing dominant role exceeds 55% and slashing pool rewards is overreach.",
1033
1031
  check(metrics, _thresholds) {
1034
1032
  const { roleShares, avgSatisfaction } = metrics;
1035
1033
  if (avgSatisfaction < 45) {
@@ -1054,7 +1052,7 @@ var P8_RegulatorCannotFightDesign = {
1054
1052
  }
1055
1053
  };
1056
1054
  var INCENTIVE_PRINCIPLES = [
1057
- P5_ProfitabilityIsCompetitive,
1055
+ P5_ProfitabilityIsRelative,
1058
1056
  P6_CrowdingMultiplierOnAllRoles,
1059
1057
  P7_NonSpecialistsSubsidiseSpecialists,
1060
1058
  P8_RegulatorCannotFightDesign
@@ -1250,7 +1248,7 @@ var P13_PotsAreZeroSumAndSelfRegulate = {
1250
1248
  id: "P13",
1251
1249
  name: "Pots Self-Regulate with Correct Multiplier",
1252
1250
  category: "currency",
1253
- description: "Competitive pot math: winRate \xD7 multiplier > (1 - houseCut) drains the pot. At 65% win rate, multiplier must be \u2264 1.38. We use 1.5 for slight surplus buffer.",
1251
+ description: "Pool math: winRate \xD7 multiplier > (1 - operatorShare) drains the pot. At 65% win rate, multiplier must be \u2264 1.38. We use 1.5 for slight surplus buffer.",
1254
1252
  check(metrics, thresholds) {
1255
1253
  const { populationByRole } = metrics;
1256
1254
  const roleEntries = Object.entries(populationByRole).sort((a, b) => b[1] - a[1]);
@@ -1259,8 +1257,8 @@ var P13_PotsAreZeroSumAndSelfRegulate = {
1259
1257
  for (const curr of metrics.currencies) {
1260
1258
  const poolSize = currencyAmounts[curr] ?? 0;
1261
1259
  if (dominantCount > 5 && poolSize < 50) {
1262
- const { poolWinRate, poolHouseCut } = thresholds;
1263
- const maxSustainableMultiplier = (1 - poolHouseCut) / poolWinRate;
1260
+ const { poolWinRate, poolOperatorShare } = thresholds;
1261
+ const maxSustainableMultiplier = (1 - poolOperatorShare) / poolWinRate;
1264
1262
  return {
1265
1263
  violated: true,
1266
1264
  severity: 7,
@@ -1543,7 +1541,7 @@ var P19_StartingSupplyExceedsDemand = {
1543
1541
  parameterType: "reward",
1544
1542
  direction: "increase",
1545
1543
  magnitude: 0.2,
1546
- reasoning: `${mostPopulatedRole} (${population} agents) has insufficient resources (${resourcesPerAgent.toFixed(2)} per agent). Cold-start scarcity. Boost competitive pool reward to attract participation despite scarcity.`
1544
+ reasoning: `${mostPopulatedRole} (${population} agents) has insufficient resources (${resourcesPerAgent.toFixed(2)} per agent). Cold-start scarcity. Boost pool reward to attract participation despite scarcity.`
1547
1545
  },
1548
1546
  confidence: 0.75,
1549
1547
  estimatedLag: 5
@@ -2288,7 +2286,7 @@ var P35_DestructionCreatesValue = {
2288
2286
  scope: { tags: ["entry"] },
2289
2287
  direction: "decrease",
2290
2288
  magnitude: 0.1,
2291
- reasoning: `${resource} supply at ${supply} units with low destruction (sink ${sinkVolume}/t). Resources not being consumed. Lower competitive pool entry to increase resource usage.`
2289
+ reasoning: `${resource} supply at ${supply} units with low destruction (sink ${sinkVolume}/t). Resources not being consumed. Lower pool entry to increase resource usage.`
2292
2290
  },
2293
2291
  confidence: 0.7,
2294
2292
  estimatedLag: 5
@@ -2895,15 +2893,24 @@ var ALL_PRINCIPLES = [
2895
2893
  ];
2896
2894
 
2897
2895
  // src/Simulator.ts
2896
+ var DEFAULT_SIM_CONFIG = {
2897
+ sinkMultiplier: 0.2,
2898
+ faucetMultiplier: 0.15,
2899
+ frictionMultiplier: 0.1,
2900
+ frictionVelocityScale: 10,
2901
+ redistributionMultiplier: 0.3,
2902
+ neutralMultiplier: 0.05,
2903
+ minIterations: 100,
2904
+ maxProjectionTicks: 20
2905
+ };
2898
2906
  var Simulator = class {
2899
- constructor(registry) {
2907
+ constructor(registry, simConfig) {
2900
2908
  this.diagnoser = new Diagnoser(ALL_PRINCIPLES);
2901
- // Cache beforeViolations for the *current* tick only (one entry max).
2902
- // Using a Map here is intentional but the cache must be bounded — we only
2903
- // care about the tick that is currently being evaluated, so we evict any
2904
- // entries whose key differs from the incoming tick.
2905
- this.beforeViolationsCache = /* @__PURE__ */ new Map();
2909
+ // Cache beforeViolations for the *current* tick only.
2910
+ this.cachedViolationsTick = -1;
2911
+ this.cachedViolations = /* @__PURE__ */ new Set();
2906
2912
  this.registry = registry;
2913
+ this.simConfig = { ...DEFAULT_SIM_CONFIG, ...simConfig };
2907
2914
  }
2908
2915
  /**
2909
2916
  * Simulate the effect of applying `action` to the current economy forward `forwardTicks`.
@@ -2927,16 +2934,13 @@ var Simulator = class {
2927
2934
  const mean = this.averageMetrics(outcomes);
2928
2935
  const netImprovement = this.checkImprovement(currentMetrics, p50, action);
2929
2936
  const tick = currentMetrics.tick;
2930
- if (this.beforeViolationsCache.size > 0 && !this.beforeViolationsCache.has(tick)) {
2931
- this.beforeViolationsCache.clear();
2932
- }
2933
- let beforeViolations = this.beforeViolationsCache.get(tick);
2934
- if (!beforeViolations) {
2935
- beforeViolations = new Set(
2937
+ if (this.cachedViolationsTick !== tick) {
2938
+ this.cachedViolations = new Set(
2936
2939
  this.diagnoser.diagnose(currentMetrics, thresholds).map((d) => d.principle.id)
2937
2940
  );
2938
- this.beforeViolationsCache.set(tick, beforeViolations);
2941
+ this.cachedViolationsTick = tick;
2939
2942
  }
2943
+ const beforeViolations = this.cachedViolations;
2940
2944
  const afterViolations = new Set(
2941
2945
  this.diagnoser.diagnose(p50, thresholds).map((d) => d.principle.id)
2942
2946
  );
@@ -3026,21 +3030,22 @@ var Simulator = class {
3026
3030
  if (!impact) {
3027
3031
  impact = this.inferFlowImpact(action.parameterType);
3028
3032
  }
3033
+ const cfg = this.simConfig;
3029
3034
  switch (impact) {
3030
3035
  case "sink":
3031
- return sign * (metrics.netFlowByCurrency[currency] ?? 0) * 0.2;
3036
+ return sign * (metrics.netFlowByCurrency[currency] ?? 0) * cfg.sinkMultiplier;
3032
3037
  case "faucet":
3033
- return -sign * dominantRoleCount * 0.3;
3038
+ return -sign * dominantRoleCount * cfg.redistributionMultiplier;
3034
3039
  case "neutral":
3035
- return sign * dominantRoleCount * 0.5;
3040
+ return sign * dominantRoleCount * cfg.neutralMultiplier;
3036
3041
  case "mixed":
3037
- return sign * (metrics.faucetVolumeByCurrency[currency] ?? 0) * 0.15;
3042
+ return sign * (metrics.faucetVolumeByCurrency[currency] ?? 0) * cfg.faucetMultiplier;
3038
3043
  case "friction":
3039
- return sign * (metrics.netFlowByCurrency[currency] ?? 0) * 0.1;
3044
+ return sign * (metrics.netFlowByCurrency[currency] ?? 0) * cfg.frictionMultiplier;
3040
3045
  case "redistribution":
3041
- return sign * dominantRoleCount * 0.05;
3046
+ return sign * dominantRoleCount * cfg.neutralMultiplier;
3042
3047
  default:
3043
- return sign * (metrics.netFlowByCurrency[currency] ?? 0) * 0.1;
3048
+ return sign * (metrics.netFlowByCurrency[currency] ?? 0) * cfg.frictionMultiplier;
3044
3049
  }
3045
3050
  }
3046
3051
  /** Infer flow impact from parameter type when registry is unavailable */
@@ -3061,7 +3066,7 @@ var Simulator = class {
3061
3066
  return "mixed";
3062
3067
  }
3063
3068
  }
3064
- checkImprovement(before, after, action) {
3069
+ checkImprovement(before, after, _action) {
3065
3070
  const satisfactionImproved = after.avgSatisfaction >= before.avgSatisfaction - 2;
3066
3071
  const flowMoreBalanced = before.currencies.every((curr) => {
3067
3072
  const afterFlow = Math.abs(after.netFlowByCurrency[curr] ?? 0);
@@ -3073,7 +3078,6 @@ var Simulator = class {
3073
3078
  const beforeGini = before.giniCoefficientByCurrency[curr] ?? 0;
3074
3079
  return afterGini <= beforeGini + 0.05;
3075
3080
  });
3076
- void action;
3077
3081
  return satisfactionImproved && flowMoreBalanced && notWorseGini;
3078
3082
  }
3079
3083
  averageMetrics(outcomes) {
@@ -3225,6 +3229,14 @@ var Planner = class {
3225
3229
  this.cooldowns.clear();
3226
3230
  this.typeCooldowns.clear();
3227
3231
  }
3232
+ /** V1.5.2: Reset active plan count (e.g., on system restart) */
3233
+ resetActivePlans() {
3234
+ this.activePlanCount = 0;
3235
+ }
3236
+ /** V1.5.2: Current active plan count (for diagnostics) */
3237
+ getActivePlanCount() {
3238
+ return this.activePlanCount;
3239
+ }
3228
3240
  typeCooldownKey(type, scope) {
3229
3241
  const parts = [type];
3230
3242
  if (scope?.system) parts.push(`sys:${scope.system}`);
@@ -3241,8 +3253,9 @@ var Planner = class {
3241
3253
 
3242
3254
  // src/Executor.ts
3243
3255
  var Executor = class {
3244
- constructor() {
3256
+ constructor(settlementWindowTicks = 200) {
3245
3257
  this.activePlans = [];
3258
+ this.maxActiveTicks = settlementWindowTicks;
3246
3259
  }
3247
3260
  async apply(plan, adapter, currentParams) {
3248
3261
  const originalValue = currentParams[plan.parameter] ?? plan.currentValue;
@@ -3261,8 +3274,7 @@ var Executor = class {
3261
3274
  for (const active of this.activePlans) {
3262
3275
  const { plan, originalValue } = active;
3263
3276
  const rc = plan.rollbackCondition;
3264
- const maxActiveTicks = 200;
3265
- if (plan.appliedAt !== void 0 && metrics.tick - plan.appliedAt > maxActiveTicks) {
3277
+ if (plan.appliedAt !== void 0 && metrics.tick - plan.appliedAt > this.maxActiveTicks) {
3266
3278
  settled.push(plan);
3267
3279
  continue;
3268
3280
  }
@@ -3329,9 +3341,9 @@ var DecisionLog = class {
3329
3341
  reasoning: this.buildReasoning(diagnosis, plan, result),
3330
3342
  metricsSnapshot: metrics
3331
3343
  };
3332
- this.entries.unshift(entry);
3333
- if (this.entries.length > this.maxEntries) {
3334
- this.entries.pop();
3344
+ this.entries.push(entry);
3345
+ if (this.entries.length > this.maxEntries * 1.5) {
3346
+ this.entries = this.entries.slice(-this.maxEntries);
3335
3347
  }
3336
3348
  return entry;
3337
3349
  }
@@ -3346,8 +3358,10 @@ var DecisionLog = class {
3346
3358
  reasoning: reason,
3347
3359
  metricsSnapshot: metrics
3348
3360
  };
3349
- this.entries.unshift(entry);
3350
- if (this.entries.length > this.maxEntries) this.entries.pop();
3361
+ this.entries.push(entry);
3362
+ if (this.entries.length > this.maxEntries * 1.5) {
3363
+ this.entries = this.entries.slice(-this.maxEntries);
3364
+ }
3351
3365
  }
3352
3366
  query(filter) {
3353
3367
  return this.entries.filter((e) => {
@@ -3360,7 +3374,7 @@ var DecisionLog = class {
3360
3374
  });
3361
3375
  }
3362
3376
  latest(n = 30) {
3363
- return this.entries.slice(0, n);
3377
+ return this.entries.slice(-n).reverse();
3364
3378
  }
3365
3379
  export(format = "json") {
3366
3380
  if (format === "text") {
@@ -3589,10 +3603,13 @@ var MetricStore = class {
3589
3603
  var PersonaTracker = class {
3590
3604
  constructor() {
3591
3605
  this.agentHistory = /* @__PURE__ */ new Map();
3606
+ this.lastSeen = /* @__PURE__ */ new Map();
3592
3607
  }
3593
3608
  /** Ingest a state snapshot and update agent signal history */
3594
3609
  update(state) {
3610
+ const tick = state.tick;
3595
3611
  for (const agentId of Object.keys(state.agentBalances)) {
3612
+ this.lastSeen.set(agentId, tick);
3596
3613
  const history = this.agentHistory.get(agentId) ?? [];
3597
3614
  const inv = state.agentInventories[agentId] ?? {};
3598
3615
  const uniqueItems = Object.values(inv).filter((q) => q > 0).length;
@@ -3610,6 +3627,14 @@ var PersonaTracker = class {
3610
3627
  if (history.length > 50) history.shift();
3611
3628
  this.agentHistory.set(agentId, history);
3612
3629
  }
3630
+ if (tick % 50 === 0) {
3631
+ for (const [id, lastTick] of this.lastSeen) {
3632
+ if (tick - lastTick > 100) {
3633
+ this.agentHistory.delete(id);
3634
+ this.lastSeen.delete(id);
3635
+ }
3636
+ }
3637
+ }
3613
3638
  }
3614
3639
  /** Classify all agents and return persona distribution */
3615
3640
  getDistribution() {
@@ -3801,7 +3826,6 @@ var ParameterRegistry = class {
3801
3826
  var AgentE = class {
3802
3827
  constructor(config) {
3803
3828
  this.planner = new Planner();
3804
- this.executor = new Executor();
3805
3829
  this.registry = new ParameterRegistry();
3806
3830
  // ── State ──
3807
3831
  this.log = new DecisionLog();
@@ -3824,6 +3848,8 @@ var AgentE = class {
3824
3848
  dominantRoles: config.dominantRoles ?? [],
3825
3849
  idealDistribution: config.idealDistribution ?? {},
3826
3850
  validateRegistry: config.validateRegistry ?? true,
3851
+ simulation: config.simulation ?? {},
3852
+ settlementWindowTicks: config.settlementWindowTicks ?? 200,
3827
3853
  tickConfig: config.tickConfig ?? { duration: 1, unit: "tick" },
3828
3854
  gracePeriod: config.gracePeriod ?? 50,
3829
3855
  checkInterval: config.checkInterval ?? 5,
@@ -3849,7 +3875,8 @@ var AgentE = class {
3849
3875
  for (const w of validation.warnings) console.warn(`[AgentE] Registry warning: ${w}`);
3850
3876
  for (const e of validation.errors) console.error(`[AgentE] Registry error: ${e}`);
3851
3877
  }
3852
- this.simulator = new Simulator(this.registry);
3878
+ this.executor = new Executor(config.settlementWindowTicks);
3879
+ this.simulator = new Simulator(this.registry, config.simulation);
3853
3880
  if (config.onDecision) this.on("decision", config.onDecision);
3854
3881
  if (config.onAlert) this.on("alert", config.onAlert);
3855
3882
  if (config.onRollback) this.on("rollback", config.onRollback);
@@ -3885,7 +3912,7 @@ var AgentE = class {
3885
3912
  if (!this.isRunning || this.isPaused) return;
3886
3913
  const currentState = state ?? await Promise.resolve(this.adapter.getState());
3887
3914
  this.currentTick = currentState.tick;
3888
- const events = [...this.eventBuffer];
3915
+ const events = this.eventBuffer;
3889
3916
  this.eventBuffer = [];
3890
3917
  let metrics;
3891
3918
  try {
@@ -4003,7 +4030,9 @@ var AgentE = class {
4003
4030
  // ── Events ──────────────────────────────────────────────────────────────────
4004
4031
  on(event, handler) {
4005
4032
  const list = this.handlers.get(event) ?? [];
4006
- list.push(handler);
4033
+ if (!list.includes(handler)) {
4034
+ list.push(handler);
4035
+ }
4007
4036
  this.handlers.set(event, list);
4008
4037
  return this;
4009
4038
  }
@@ -4016,8 +4045,12 @@ var AgentE = class {
4016
4045
  const list = this.handlers.get(event) ?? [];
4017
4046
  let result;
4018
4047
  for (const handler of list) {
4019
- result = handler(...args);
4020
- if (result === false) return false;
4048
+ try {
4049
+ result = handler(...args);
4050
+ if (result === false) return false;
4051
+ } catch (err) {
4052
+ console.error(`[AgentE] Handler error on '${event}':`, err);
4053
+ }
4021
4054
  }
4022
4055
  return result;
4023
4056
  }
@@ -4475,7 +4508,7 @@ function describeValue(value) {
4475
4508
  P57_CombinatorialPriceSpace,
4476
4509
  P58_NoNaturalNumeraire,
4477
4510
  P59_GiftEconomyNoise,
4478
- P5_ProfitabilityIsCompetitive,
4511
+ P5_ProfitabilityIsRelative,
4479
4512
  P60_SurplusDisposalAsymmetry,
4480
4513
  P6_CrowdingMultiplierOnAllRoles,
4481
4514
  P7_NonSpecialistsSubsidiseSpecialists,