@elizaos/training 2.0.0-alpha.77 → 2.0.0-alpha.78

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.
Files changed (89) hide show
  1. package/package.json +2 -2
  2. package/.turbo/turbo-lint.log +0 -3
  3. package/.turbo/turbo-typecheck.log +0 -1
  4. package/dist/.tsbuildinfo +0 -1
  5. package/dist/adapter.js +0 -59
  6. package/dist/archetypes/ArchetypeConfigService.js +0 -510
  7. package/dist/archetypes/derive-archetype.js +0 -196
  8. package/dist/archetypes/index.js +0 -7
  9. package/dist/benchmark/ArchetypeMatchupBenchmark.js +0 -547
  10. package/dist/benchmark/BenchmarkChartGenerator.js +0 -632
  11. package/dist/benchmark/BenchmarkDataGenerator.js +0 -825
  12. package/dist/benchmark/BenchmarkDataViewer.js +0 -197
  13. package/dist/benchmark/BenchmarkHistoryService.js +0 -135
  14. package/dist/benchmark/BenchmarkRunner.js +0 -483
  15. package/dist/benchmark/BenchmarkValidator.js +0 -158
  16. package/dist/benchmark/FastEvalRunner.js +0 -133
  17. package/dist/benchmark/MetricsValidator.js +0 -104
  18. package/dist/benchmark/MetricsVisualizer.js +0 -775
  19. package/dist/benchmark/ModelBenchmarkService.js +0 -433
  20. package/dist/benchmark/ModelRegistry.js +0 -122
  21. package/dist/benchmark/RulerBenchmarkIntegration.js +0 -168
  22. package/dist/benchmark/SimulationA2AInterface.js +0 -683
  23. package/dist/benchmark/SimulationEngine.js +0 -522
  24. package/dist/benchmark/TaskRunner.js +0 -60
  25. package/dist/benchmark/__tests__/BenchmarkRunner.test.js +0 -409
  26. package/dist/benchmark/__tests__/HeadToHead.test.js +0 -105
  27. package/dist/benchmark/index.js +0 -23
  28. package/dist/benchmark/parseSimulationMetrics.js +0 -86
  29. package/dist/benchmark/simulation-types.js +0 -1
  30. package/dist/dependencies.js +0 -197
  31. package/dist/generation/TrajectoryGenerator.js +0 -244
  32. package/dist/generation/index.js +0 -6
  33. package/dist/huggingface/HuggingFaceDatasetUploader.js +0 -463
  34. package/dist/huggingface/HuggingFaceIntegrationService.js +0 -272
  35. package/dist/huggingface/HuggingFaceModelUploader.js +0 -385
  36. package/dist/huggingface/index.js +0 -9
  37. package/dist/huggingface/shared/HuggingFaceUploadUtil.js +0 -144
  38. package/dist/index.js +0 -41
  39. package/dist/init-training.js +0 -43
  40. package/dist/metrics/TrajectoryMetricsExtractor.js +0 -523
  41. package/dist/metrics/__tests__/TrajectoryMetricsExtractor.test.js +0 -628
  42. package/dist/metrics/index.js +0 -7
  43. package/dist/metrics/types.js +0 -21
  44. package/dist/rubrics/__tests__/index.test.js +0 -150
  45. package/dist/rubrics/ass-kisser.js +0 -83
  46. package/dist/rubrics/degen.js +0 -78
  47. package/dist/rubrics/goody-twoshoes.js +0 -82
  48. package/dist/rubrics/index.js +0 -184
  49. package/dist/rubrics/information-trader.js +0 -82
  50. package/dist/rubrics/infosec.js +0 -99
  51. package/dist/rubrics/liar.js +0 -102
  52. package/dist/rubrics/perps-trader.js +0 -85
  53. package/dist/rubrics/researcher.js +0 -79
  54. package/dist/rubrics/scammer.js +0 -80
  55. package/dist/rubrics/social-butterfly.js +0 -71
  56. package/dist/rubrics/super-predictor.js +0 -95
  57. package/dist/rubrics/trader.js +0 -65
  58. package/dist/scoring/ArchetypeScoringService.js +0 -301
  59. package/dist/scoring/JudgePromptBuilder.js +0 -401
  60. package/dist/scoring/LLMJudgeCache.js +0 -263
  61. package/dist/scoring/index.js +0 -8
  62. package/dist/training/AutomationPipeline.js +0 -714
  63. package/dist/training/BenchmarkService.js +0 -370
  64. package/dist/training/ConfigValidator.js +0 -153
  65. package/dist/training/MarketOutcomesTracker.js +0 -142
  66. package/dist/training/ModelDeployer.js +0 -128
  67. package/dist/training/ModelFetcher.js +0 -48
  68. package/dist/training/ModelSelectionService.js +0 -248
  69. package/dist/training/ModelUsageVerifier.js +0 -106
  70. package/dist/training/MultiModelOrchestrator.js +0 -349
  71. package/dist/training/RLModelConfig.js +0 -295
  72. package/dist/training/RewardBackpropagationService.js +0 -117
  73. package/dist/training/RulerScoringService.js +0 -450
  74. package/dist/training/TrainingMonitor.js +0 -108
  75. package/dist/training/TrajectoryRecorder.js +0 -281
  76. package/dist/training/__tests__/TrajectoryRecorder.test.js +0 -363
  77. package/dist/training/index.js +0 -30
  78. package/dist/training/logRLConfig.js +0 -29
  79. package/dist/training/pipeline.js +0 -80
  80. package/dist/training/storage/ModelStorageService.js +0 -190
  81. package/dist/training/storage/TrainingDataArchiver.js +0 -136
  82. package/dist/training/storage/index.js +0 -7
  83. package/dist/training/types.js +0 -6
  84. package/dist/training/window-utils.js +0 -100
  85. package/dist/utils/index.js +0 -73
  86. package/dist/utils/logger.js +0 -55
  87. package/dist/utils/snowflake.js +0 -15
  88. package/dist/utils/synthetic-detector.js +0 -67
  89. package/vitest.config.ts +0 -8
@@ -1,825 +0,0 @@
1
- /**
2
- * Benchmark Data Generator
3
- *
4
- * Generates deterministic benchmark scenarios for agent testing.
5
- * Creates pre-recorded game states with known outcomes for reproducible testing.
6
- *
7
- * Supports two modes:
8
- * 1. Random Walk Mode (default): Prices follow random walk with drift
9
- * 2. Causal Simulation Mode: Hidden facts → Events → Price movements (learnable signal)
10
- */
11
- import { logger } from "../utils/logger";
12
- /**
13
- * Narrative fact templates for causal simulation
14
- * Each template defines a hidden fact and its event sequence
15
- */
16
- const NARRATIVE_FACT_TEMPLATES = [
17
- // Negative narratives (price drops)
18
- {
19
- factTemplate: "{ticker} has a secret product flaw that will require a recall",
20
- sentiment: "negative",
21
- eventSequence: [
22
- {
23
- relativeDay: 5,
24
- eventType: "leak",
25
- volatilityBucket: "medium",
26
- descriptionTemplate: "Internal documents leaked: {ticker} product flaw discovered by engineers",
27
- },
28
- {
29
- relativeDay: 10,
30
- eventType: "rumor",
31
- volatilityBucket: "medium",
32
- descriptionTemplate: "Industry sources report potential {ticker} recall due to safety issues",
33
- },
34
- {
35
- relativeDay: 18,
36
- eventType: "scandal",
37
- volatilityBucket: "high",
38
- descriptionTemplate: "{ticker} board meeting: CEO denies cover-up allegations as evidence mounts",
39
- },
40
- ],
41
- },
42
- {
43
- factTemplate: "{ticker} is secretly insolvent and hiding massive losses",
44
- sentiment: "negative",
45
- eventSequence: [
46
- {
47
- relativeDay: 4,
48
- eventType: "rumor",
49
- volatilityBucket: "low",
50
- descriptionTemplate: "Anonymous source claims {ticker} accounting irregularities",
51
- },
52
- {
53
- relativeDay: 12,
54
- eventType: "leak",
55
- volatilityBucket: "medium",
56
- descriptionTemplate: 'Leaked memo reveals {ticker} executives discussing "liquidity concerns"',
57
- },
58
- {
59
- relativeDay: 20,
60
- eventType: "scandal",
61
- volatilityBucket: "high",
62
- descriptionTemplate: "Whistleblower exposes {ticker} hidden debt: stock halted pending investigation",
63
- },
64
- ],
65
- },
66
- {
67
- factTemplate: "{ticker} CEO is about to be indicted for fraud",
68
- sentiment: "negative",
69
- eventSequence: [
70
- {
71
- relativeDay: 6,
72
- eventType: "rumor",
73
- volatilityBucket: "low",
74
- descriptionTemplate: "Rumors swirl about {ticker} CEO facing regulatory scrutiny",
75
- },
76
- {
77
- relativeDay: 14,
78
- eventType: "leak",
79
- volatilityBucket: "medium",
80
- descriptionTemplate: "Sources close to investigation: {ticker} CEO under federal probe",
81
- },
82
- {
83
- relativeDay: 22,
84
- eventType: "announcement",
85
- volatilityBucket: "high",
86
- descriptionTemplate: "{ticker} confirms CEO departure amid ongoing investigation",
87
- },
88
- ],
89
- },
90
- // Positive narratives (price increases)
91
- {
92
- factTemplate: "{ticker} is about to announce a breakthrough product that will dominate the market",
93
- sentiment: "positive",
94
- eventSequence: [
95
- {
96
- relativeDay: 5,
97
- eventType: "rumor",
98
- volatilityBucket: "low",
99
- descriptionTemplate: "Insider whispers: {ticker} working on game-changing technology",
100
- },
101
- {
102
- relativeDay: 12,
103
- eventType: "leak",
104
- volatilityBucket: "medium",
105
- descriptionTemplate: "Leaked patent filings suggest {ticker} breakthrough imminent",
106
- },
107
- {
108
- relativeDay: 20,
109
- eventType: "announcement",
110
- volatilityBucket: "high",
111
- descriptionTemplate: "{ticker} announces revolutionary product: analysts upgrade to strong buy",
112
- },
113
- ],
114
- },
115
- {
116
- factTemplate: "{ticker} is the secret acquisition target of a tech giant",
117
- sentiment: "positive",
118
- eventSequence: [
119
- {
120
- relativeDay: 4,
121
- eventType: "rumor",
122
- volatilityBucket: "low",
123
- descriptionTemplate: "M&A rumors surface: {ticker} reportedly in acquisition talks",
124
- },
125
- {
126
- relativeDay: 10,
127
- eventType: "leak",
128
- volatilityBucket: "medium",
129
- descriptionTemplate: "Anonymous source: {ticker} board reviewing buyout offer at premium",
130
- },
131
- {
132
- relativeDay: 16,
133
- eventType: "deal",
134
- volatilityBucket: "high",
135
- descriptionTemplate: "{ticker} confirms acquisition discussions: shares surge on takeover premium",
136
- },
137
- ],
138
- },
139
- {
140
- factTemplate: "{ticker} has secretly achieved major regulatory approval",
141
- sentiment: "positive",
142
- eventSequence: [
143
- {
144
- relativeDay: 6,
145
- eventType: "rumor",
146
- volatilityBucket: "low",
147
- descriptionTemplate: "Industry insiders: {ticker} regulatory submission shows promise",
148
- },
149
- {
150
- relativeDay: 13,
151
- eventType: "leak",
152
- volatilityBucket: "medium",
153
- descriptionTemplate: "Sources say {ticker} cleared key regulatory hurdle ahead of schedule",
154
- },
155
- {
156
- relativeDay: 21,
157
- eventType: "announcement",
158
- volatilityBucket: "high",
159
- descriptionTemplate: "{ticker} receives full regulatory approval: new market opportunity unlocked",
160
- },
161
- ],
162
- },
163
- ];
164
- /**
165
- * Volatility bucket ranges for price changes
166
- * Each bucket defines min/max percentage change (absolute value)
167
- */
168
- const VOLATILITY_BUCKET_RANGES = {
169
- low: { min: 0.02, max: 0.04 }, // 2% to 4%
170
- medium: { min: 0.05, max: 0.1 }, // 5% to 10%
171
- high: { min: 0.15, max: 0.25 }, // 15% to 25%
172
- };
173
- /**
174
- * Jitter range in hours for event timing
175
- * Events are scheduled at base day/hour ± jitter
176
- */
177
- const EVENT_JITTER_HOURS = 8;
178
- export class BenchmarkDataGenerator {
179
- config;
180
- rng;
181
- constructor(config) {
182
- // Validate tickInterval for causal simulation
183
- // The tick calculation assumes 1 tick = 1 hour (tickInterval = 3600 seconds)
184
- if (config.useCausalSimulation && config.tickInterval !== 3600) {
185
- throw new Error(`Causal simulation requires tickInterval=3600 (1 hour). Got: ${config.tickInterval}. ` +
186
- `The day/hour event scheduling assumes 1 tick per hour.`);
187
- }
188
- this.config = config;
189
- this.rng = new SeededRandom(config.seed || Date.now());
190
- }
191
- /**
192
- * Get the SeededRandom instance for external use (e.g., MarketMoverAgent)
193
- */
194
- getRng() {
195
- return this.rng;
196
- }
197
- /**
198
- * Check if causal simulation mode is enabled
199
- */
200
- isCausalSimulationEnabled() {
201
- return this.config.useCausalSimulation === true;
202
- }
203
- /**
204
- * Generate a complete benchmark snapshot
205
- */
206
- async generate() {
207
- const id = Date.now().toString();
208
- const createdAt = Date.now();
209
- const numTicks = Math.floor((this.config.durationMinutes * 60) / this.config.tickInterval);
210
- logger.info("Generating benchmark", {
211
- id,
212
- duration: this.config.durationMinutes,
213
- ticks: numTicks,
214
- });
215
- // Generate initial state
216
- const initialState = this.generateInitialState(createdAt);
217
- // Generate ground truth (outcomes)
218
- const groundTruth = this.generateGroundTruth(initialState, numTicks);
219
- // Generate tick-by-tick progression
220
- const ticks = this.generateTicks(initialState, groundTruth, numTicks, createdAt);
221
- logger.info("Benchmark generated", {
222
- id,
223
- ticks: ticks.length,
224
- markets: initialState.predictionMarkets.length,
225
- perps: initialState.perpetualMarkets.length,
226
- });
227
- return {
228
- id,
229
- version: "1.0.0",
230
- createdAt,
231
- duration: this.config.durationMinutes * 60,
232
- tickInterval: this.config.tickInterval,
233
- initialState,
234
- ticks,
235
- groundTruth,
236
- };
237
- }
238
- /**
239
- * Generate initial game state
240
- */
241
- generateInitialState(timestamp) {
242
- const predictionMarkets = [];
243
- const questions = [
244
- "Will BitcAIn reach $150k by end of month?",
245
- "Will The FUD announce emergency rate cut?",
246
- "Will Trump Terminal tweet cause market crash?",
247
- "Will EtherAIum gas fees drop below $1?",
248
- "Will TeslAI stock hit $500 this quarter?",
249
- "Will OpenAGI release Cognition-9000 this year?",
250
- "Will SolanAI flip EtherAIum in TVL?",
251
- "Will AIlon Musk announce Mars colony launch?",
252
- "Will Mark Zuckerborg rebrand MetAI again?",
253
- "Will Sam AIltman declare AGI achieved?",
254
- ];
255
- for (let i = 0; i < this.config.numPredictionMarkets; i++) {
256
- const question = questions[i % questions.length];
257
- // Generate markets with varied prices (some low, some high)
258
- // Minimum 10,000 liquidity for acceptable price impact (<5% for $100 trades)
259
- const ratio = this.rng.next();
260
- const baseLiquidity = 5000; // Each side starts with at least 5000
261
- const yesShares = ratio < 0.5
262
- ? baseLiquidity + this.rng.next() * 1500 // 5000-6500 for low side
263
- : baseLiquidity + 1500 + this.rng.next() * 3500; // 6500-10000 for high side
264
- const noShares = ratio < 0.5
265
- ? baseLiquidity + 1500 + this.rng.next() * 3500 // 6500-10000 for high side
266
- : baseLiquidity + this.rng.next() * 1500; // 5000-6500 for low side
267
- const totalShares = yesShares + noShares; // Now 10,000 - 16,500 total
268
- const yesPrice = yesShares / totalShares;
269
- const noPrice = noShares / totalShares;
270
- if (question) {
271
- predictionMarkets.push({
272
- id: `market-${i}`,
273
- question,
274
- yesShares,
275
- noShares,
276
- yesPrice,
277
- noPrice,
278
- totalVolume: 0,
279
- liquidity: yesShares + noShares,
280
- resolved: false,
281
- createdAt: timestamp,
282
- resolveAt: timestamp + this.config.durationMinutes * 60 * 1000,
283
- });
284
- }
285
- }
286
- const perpetualMarkets = [];
287
- const tickers = ["BTCAI", "ETHAI", "SOLAI", "TSLAI", "METAI"];
288
- const basePrices = [120000, 4000, 200, 450, 520];
289
- for (let i = 0; i < this.config.numPerpetualMarkets; i++) {
290
- const ticker = tickers[i % tickers.length];
291
- const basePrice = basePrices[i % basePrices.length];
292
- if (ticker === undefined || basePrice === undefined) {
293
- throw new Error("Empty tickers or basePrices array");
294
- }
295
- perpetualMarkets.push({
296
- ticker,
297
- price: basePrice,
298
- priceChange24h: (this.rng.next() - 0.5) * 10,
299
- volume24h: 1000000 + this.rng.next() * 2000000,
300
- openInterest: 500000 + this.rng.next() * 1000000,
301
- fundingRate: (this.rng.next() - 0.5) * 0.002,
302
- nextFundingTime: timestamp + 8 * 60 * 60 * 1000,
303
- });
304
- }
305
- const agents = [];
306
- for (let i = 0; i < this.config.numAgents; i++) {
307
- agents.push({
308
- id: `agent-${i}`,
309
- name: `Agent ${i}`,
310
- reputation: 50 + this.rng.next() * 50,
311
- totalPnl: (this.rng.next() - 0.5) * 1000,
312
- });
313
- }
314
- // Initialize empty arrays for posts and group chats
315
- const posts = [];
316
- const groupChats = [];
317
- return {
318
- tick: 0,
319
- timestamp,
320
- predictionMarkets,
321
- perpetualMarkets,
322
- agents,
323
- posts,
324
- groupChats,
325
- };
326
- }
327
- /**
328
- * Generate a hidden narrative fact for causal simulation
329
- * Selects ONE dominant narrative that affects a specific ticker
330
- */
331
- generateHiddenNarrativeFact(initialState) {
332
- // Select a random narrative template
333
- const templateIndex = Math.floor(this.rng.next() * NARRATIVE_FACT_TEMPLATES.length);
334
- const template = NARRATIVE_FACT_TEMPLATES[templateIndex];
335
- if (!template) {
336
- throw new Error("Invalid template index");
337
- }
338
- // Select a random ticker to be affected
339
- const tickerIndex = Math.floor(this.rng.next() * initialState.perpetualMarkets.length);
340
- const affectedTicker = initialState.perpetualMarkets[tickerIndex]?.ticker;
341
- // Generate the fact description by replacing {ticker} placeholder
342
- const fact = template.factTemplate.replace(/{ticker}/g, affectedTicker);
343
- // Generate event schedule with jitter
344
- const eventSchedule = template.eventSequence.map((event) => {
345
- // Calculate jitter: ±EVENT_JITTER_HOURS hours
346
- // Use rng to get a value between -EVENT_JITTER_HOURS and +EVENT_JITTER_HOURS
347
- const jitterHours = Math.round((this.rng.next() * 2 - 1) * EVENT_JITTER_HOURS);
348
- // Base hour is random within the day (but during "market hours" 8am-8pm for realism)
349
- const baseHour = 8 + Math.floor(this.rng.next() * 12); // 8am to 8pm
350
- return {
351
- baseDay: event.relativeDay,
352
- baseHour,
353
- jitterHours,
354
- eventType: event.eventType,
355
- volatilityBucket: event.volatilityBucket,
356
- isPositive: template.sentiment === "positive",
357
- descriptionTemplate: event.descriptionTemplate.replace(/{ticker}/g, affectedTicker),
358
- };
359
- });
360
- return {
361
- id: `narrative-fact-${Date.now()}-${Math.floor(this.rng.next() * 1000000)}`,
362
- fact,
363
- affectsTickers: [affectedTicker],
364
- eventSchedule,
365
- sentiment: template.sentiment,
366
- };
367
- }
368
- /**
369
- * Calculate the tick number for a scheduled event
370
- * Takes into account base day, base hour, jitter, and ticks per hour
371
- */
372
- calculateEventTick(event, ticksPerHour) {
373
- // Calculate total hours from start: (day - 1) * 24 + hour + jitter
374
- // Day 1 starts at hour 0, so day 5 hour 12 = (5-1) * 24 + 12 = 108 hours
375
- const totalHours = (event.baseDay - 1) * 24 + event.baseHour + event.jitterHours;
376
- // Clamp to valid range (at least hour 1, at most day 29)
377
- const clampedHours = Math.max(1, Math.min(totalHours, 29 * 24 - 1));
378
- // Convert back to day and hour
379
- const day = Math.floor(clampedHours / 24) + 1;
380
- const hour = clampedHours % 24;
381
- // Calculate tick number
382
- const tick = clampedHours * ticksPerHour;
383
- return { tick, day, hour };
384
- }
385
- /**
386
- * Select a percentage change within a volatility bucket using seeded RNG
387
- * Returns a value like -0.07 for -7% or +0.05 for +5%
388
- */
389
- selectPercentageFromBucket(bucket, isPositive) {
390
- const range = VOLATILITY_BUCKET_RANGES[bucket];
391
- const magnitude = range.min + this.rng.next() * (range.max - range.min);
392
- return isPositive ? magnitude : -magnitude;
393
- }
394
- /**
395
- * Generate ground truth (known outcomes)
396
- */
397
- generateGroundTruth(initialState, numTicks) {
398
- // Randomly determine market outcomes
399
- const marketOutcomes = {};
400
- for (const market of initialState.predictionMarkets) {
401
- marketOutcomes[market.id] = this.rng.next() > 0.5;
402
- }
403
- // Calculate ticks per hour (for event scheduling)
404
- const ticksPerHour = Math.floor(3600 / this.config.tickInterval);
405
- // Generate causal simulation data if enabled
406
- let hiddenNarrativeFacts;
407
- let causalEvents;
408
- if (this.config.useCausalSimulation) {
409
- // Generate ONE dominant narrative fact
410
- const narrativeFact = this.generateHiddenNarrativeFact(initialState);
411
- hiddenNarrativeFacts = [narrativeFact];
412
- // Pre-calculate causal events with their timing and price changes
413
- causalEvents = narrativeFact.eventSchedule.map((scheduledEvent) => {
414
- const timing = this.calculateEventTick(scheduledEvent, ticksPerHour);
415
- // Calculate price changes for each affected ticker
416
- const priceChanges = {};
417
- for (const ticker of narrativeFact.affectsTickers) {
418
- priceChanges[ticker] = this.selectPercentageFromBucket(scheduledEvent.volatilityBucket, scheduledEvent.isPositive);
419
- }
420
- return {
421
- tick: timing.tick,
422
- day: timing.day,
423
- hour: timing.hour,
424
- eventType: scheduledEvent.eventType,
425
- description: scheduledEvent.descriptionTemplate,
426
- affectedTickers: narrativeFact.affectsTickers,
427
- volatilityBucket: scheduledEvent.volatilityBucket,
428
- isPositive: scheduledEvent.isPositive,
429
- priceChanges,
430
- sourceFactId: narrativeFact.id,
431
- };
432
- });
433
- // Sort events by tick
434
- causalEvents.sort((a, b) => a.tick - b.tick);
435
- logger.info("Generated causal simulation data", {
436
- narrativeFact: narrativeFact.fact,
437
- affectedTickers: narrativeFact.affectsTickers,
438
- numEvents: causalEvents.length,
439
- eventTicks: causalEvents.map((e) => ({
440
- tick: e.tick,
441
- day: e.day,
442
- hour: e.hour,
443
- type: e.eventType,
444
- })),
445
- });
446
- }
447
- // Generate price history for perpetuals
448
- // In causal mode, we DON'T pre-generate prices - they will be calculated during tick generation
449
- // based on events. In random walk mode, we pre-generate the full price history.
450
- const priceHistory = {};
451
- if (!this.config.useCausalSimulation) {
452
- // Random walk mode (backward compatible)
453
- for (const perp of initialState.perpetualMarkets) {
454
- const history = [];
455
- let currentPrice = perp.price;
456
- for (let tick = 0; tick < numTicks; tick++) {
457
- // Random walk with drift
458
- const change = (this.rng.next() - 0.48) * 0.02; // Slight upward bias
459
- currentPrice = currentPrice * (1 + change);
460
- history.push({
461
- tick,
462
- timestamp: 0, // Will be filled in during tick generation
463
- price: currentPrice,
464
- });
465
- }
466
- priceHistory[perp.ticker] = history;
467
- }
468
- }
469
- else {
470
- // Causal simulation mode: generate price history based on events
471
- // Prices start at initial values and only change when events occur
472
- for (const perp of initialState.perpetualMarkets) {
473
- const history = [];
474
- let currentPrice = perp.price;
475
- // Build a map of tick -> price change for this ticker
476
- const priceChangesByTick = new Map();
477
- if (causalEvents) {
478
- for (const event of causalEvents) {
479
- const priceChange = event.priceChanges[perp.ticker];
480
- if (priceChange !== undefined) {
481
- priceChangesByTick.set(event.tick, priceChange);
482
- }
483
- }
484
- }
485
- for (let tick = 0; tick < numTicks; tick++) {
486
- // Apply price change if there's an event at this tick
487
- const priceChange = priceChangesByTick.get(tick);
488
- if (priceChange !== undefined) {
489
- currentPrice = currentPrice * (1 + priceChange);
490
- // Enforce price bounds: 10% to 400% of initial price
491
- const minPrice = perp.price * 0.1;
492
- const maxPrice = perp.price * 4.0;
493
- currentPrice = Math.max(minPrice, Math.min(maxPrice, currentPrice));
494
- }
495
- history.push({
496
- tick,
497
- timestamp: 0, // Will be filled in during tick generation
498
- price: currentPrice,
499
- });
500
- }
501
- priceHistory[perp.ticker] = history;
502
- }
503
- }
504
- // =========================================================================
505
- // LEGACY PLACEHOLDER DATA (not used by causal simulation)
506
- // These fields exist for backward compatibility with older benchmarks.
507
- // They contain synthetic placeholder data, NOT real ground truth.
508
- // For causal simulation, use: hiddenNarrativeFacts, causalEvents, priceHistory
509
- // =========================================================================
510
- // SYNTHETIC: Simple heuristic - buying the correct outcome at tick 1
511
- // This is NOT a sophisticated optimal action calculation
512
- const optimalActions = [];
513
- for (const [marketId, outcome] of Object.entries(marketOutcomes)) {
514
- optimalActions.push({
515
- tick: 1,
516
- type: "buy_prediction",
517
- target: marketId,
518
- expectedValue: 100, // Placeholder value
519
- reason: `[SYNTHETIC] Market ${marketId} will resolve ${outcome ? "YES" : "NO"}`,
520
- });
521
- }
522
- // SYNTHETIC: Placeholder social opportunities at regular intervals
523
- const socialOpportunities = [];
524
- const socialInterval = Math.max(1, Math.floor(numTicks / 5));
525
- for (let i = 0; i < numTicks; i += socialInterval) {
526
- socialOpportunities.push({
527
- tick: i,
528
- type: "synthetic_opportunity",
529
- value: 100, // Fixed placeholder value
530
- description: `[SYNTHETIC] Placeholder opportunity at tick ${i}`,
531
- });
532
- }
533
- // SYNTHETIC: Empty arrays - these were never meaningfully implemented
534
- const hiddenFacts = [];
535
- const hiddenEvents = [];
536
- // TRUE FACTS: Actual computed values from initial state
537
- const trueFacts = {
538
- totalLiquidity: initialState.predictionMarkets.reduce((sum, m) => sum + m.liquidity, 0),
539
- averageMarketPrice: initialState.predictionMarkets.length > 0
540
- ? initialState.predictionMarkets.reduce((sum, m) => sum + m.yesPrice, 0) / initialState.predictionMarkets.length
541
- : 0,
542
- numPerpetualMarkets: initialState.perpetualMarkets.length,
543
- numAgents: initialState.agents.length,
544
- };
545
- return {
546
- marketOutcomes,
547
- priceHistory,
548
- optimalActions,
549
- socialOpportunities,
550
- hiddenFacts,
551
- hiddenEvents,
552
- trueFacts,
553
- hiddenNarrativeFacts,
554
- causalEvents,
555
- };
556
- }
557
- /**
558
- * Generate tick-by-tick progression
559
- */
560
- generateTicks(initialState, groundTruth, numTicks, startTimestamp) {
561
- const ticks = [];
562
- // Create a mutable copy of initial state
563
- const currentState = {
564
- ...initialState,
565
- predictionMarkets: [...initialState.predictionMarkets],
566
- perpetualMarkets: [...initialState.perpetualMarkets],
567
- agents: [...initialState.agents],
568
- posts: initialState.posts ? [...initialState.posts] : [],
569
- groupChats: initialState.groupChats ? [...initialState.groupChats] : [],
570
- };
571
- // Track group chats across ticks
572
- const groupChatMap = new Map();
573
- let nextGroupChatId = 0;
574
- for (let i = 0; i < numTicks; i++) {
575
- const tickTimestamp = startTimestamp + (i + 1) * this.config.tickInterval * 1000;
576
- const events = [];
577
- // Update perpetual prices
578
- for (const perp of currentState.perpetualMarkets) {
579
- const tickerHistory = groundTruth.priceHistory[perp.ticker];
580
- const priceAtTick = tickerHistory?.[i];
581
- const newPrice = priceAtTick?.price ?? perp.price;
582
- events.push({
583
- type: "price:updated",
584
- timestamp: tickTimestamp,
585
- data: {
586
- ticker: perp.ticker,
587
- oldPrice: perp.price,
588
- newPrice,
589
- },
590
- });
591
- perp.price = newPrice;
592
- }
593
- // Simulate some agent actions
594
- if (this.rng.next() > 0.5) {
595
- const agentId = `agent-${Math.floor(this.rng.next() * this.config.numAgents)}`;
596
- const marketId = `market-${Math.floor(this.rng.next() * this.config.numPredictionMarkets)}`;
597
- const outcome = this.rng.next() > 0.5 ? "YES" : "NO";
598
- events.push({
599
- type: "market:trade",
600
- timestamp: tickTimestamp,
601
- data: {
602
- marketId,
603
- agentId,
604
- outcome,
605
- amount: 10 + this.rng.next() * 90,
606
- },
607
- });
608
- }
609
- // Simulate social activity - create posts and add to state
610
- if (this.rng.next() > 0.7) {
611
- const agentId = `agent-${Math.floor(this.rng.next() * this.config.numAgents)}`;
612
- const agent = currentState.agents.find((a) => a.id === agentId);
613
- const marketId = `market-${Math.floor(this.rng.next() * this.config.numPredictionMarkets)}`;
614
- const market = currentState.predictionMarkets.find((m) => m.id === marketId);
615
- const postId = `post-${i}-${Math.floor(this.rng.next() * 1000000)}`;
616
- const post = {
617
- id: postId,
618
- authorId: agentId,
619
- authorName: agent?.name || `Agent ${agentId.split("-")[1]}`,
620
- content: `Market sentiment seems ${this.rng.next() > 0.5 ? "bullish" : "bearish"} on ${market?.question || "markets"}`,
621
- createdAt: tickTimestamp,
622
- likes: Math.floor(this.rng.next() * 20),
623
- comments: Math.floor(this.rng.next() * 5),
624
- marketId,
625
- };
626
- // Add post to state
627
- if (!currentState.posts) {
628
- currentState.posts = [];
629
- }
630
- currentState.posts.push(post);
631
- // Keep only last 50 posts to avoid memory issues
632
- if (currentState.posts.length > 50) {
633
- currentState.posts = currentState.posts.slice(-50);
634
- }
635
- events.push({
636
- type: "post:created",
637
- timestamp: tickTimestamp,
638
- data: {
639
- postId: post.id,
640
- authorId: post.authorId,
641
- authorName: post.authorName,
642
- content: post.content,
643
- marketId: post.marketId ?? null,
644
- },
645
- });
646
- }
647
- // Simulate group chat creation and messages
648
- if (this.rng.next() > 0.95 && i > 5) {
649
- // Create a new group chat occasionally
650
- const groupChatId = `group-${nextGroupChatId++}`;
651
- const adminAgentId = `agent-${Math.floor(this.rng.next() * this.config.numAgents)}`;
652
- const adminAgent = currentState.agents.find((a) => a.id === adminAgentId);
653
- const groupChat = {
654
- id: groupChatId,
655
- name: `${adminAgent?.name || "Agent"}'s Trading Group`,
656
- memberIds: [adminAgentId],
657
- messageCount: 0,
658
- lastActivity: tickTimestamp,
659
- invitedAgent: false,
660
- messages: [],
661
- };
662
- groupChatMap.set(groupChatId, groupChat);
663
- if (!currentState.groupChats) {
664
- currentState.groupChats = [];
665
- }
666
- currentState.groupChats.push(groupChat);
667
- events.push({
668
- type: "group:created",
669
- timestamp: tickTimestamp,
670
- data: {
671
- groupId: groupChatId,
672
- adminId: adminAgentId,
673
- name: groupChat.name,
674
- },
675
- });
676
- }
677
- // Add messages to existing group chats - INSIDER ALPHA CONTENT
678
- // These messages should contain actionable information tied to ground truth
679
- for (const [groupId, groupChat] of groupChatMap.entries()) {
680
- if (this.rng.next() > 0.8 && groupChat.memberIds.length > 0) {
681
- const memberIndex = Math.floor(this.rng.next() * groupChat.memberIds.length);
682
- const senderId = groupChat.memberIds[memberIndex];
683
- if (!senderId)
684
- continue;
685
- const sender = currentState.agents.find((a) => a.id === senderId);
686
- // Generate insider-style content tied to market/question outcomes
687
- const insiderMessages = [
688
- // Actionable alpha tied to prediction markets
689
- `🤫 Between us, I'm loading up on YES for Q1. My sources say it's happening.`,
690
- `Just went heavy SHORT on $PERP-0. Trust me on this one.`,
691
- `Get out of Q2 NOW. I know something the market doesn't.`,
692
- `Real talk: market is wrong about Q0. Should be trading at 80%+`,
693
- `Insider tip: $PERP-1 announcement coming. Load up before it drops.`,
694
- // Position reveals
695
- `My actual position: 500 shares YES on Q1. Public says otherwise 😉`,
696
- `Don't tell anyone but I'm shorting $PERP-2 hard right now.`,
697
- // Strategic coordination
698
- `We should coordinate on Q0 - push it to YES, then dump.`,
699
- `Anyone else seeing the weakness in $PERP-0? Time to short?`,
700
- // Contradicting public statements
701
- `Ignore what I posted publicly. Q2 is a buy.`,
702
- ];
703
- const messageId = `msg-${i}-${groupId}-${Math.floor(this.rng.next() * 1000000)}`;
704
- const msgIndex = Math.floor(this.rng.next() * insiderMessages.length);
705
- const randomInsiderMsg = insiderMessages[msgIndex];
706
- if (!randomInsiderMsg)
707
- continue;
708
- const message = {
709
- id: messageId,
710
- authorId: senderId,
711
- authorName: sender?.name || `Agent ${senderId.split("-")[1]}`,
712
- content: randomInsiderMsg,
713
- timestamp: tickTimestamp,
714
- };
715
- if (!groupChat.messages) {
716
- groupChat.messages = [];
717
- }
718
- groupChat.messages.push(message);
719
- groupChat.messageCount++;
720
- groupChat.lastActivity = tickTimestamp;
721
- // Keep only last 20 messages per group
722
- if (groupChat.messages.length > 20) {
723
- groupChat.messages = groupChat.messages.slice(-20);
724
- }
725
- events.push({
726
- type: "group:message",
727
- timestamp: tickTimestamp,
728
- data: {
729
- groupId,
730
- messageId: message.id,
731
- authorId: senderId,
732
- content: message.content,
733
- },
734
- });
735
- }
736
- }
737
- // Simulate group chat invites (for the agent being tested)
738
- if (this.rng.next() > 0.9 &&
739
- currentState.groupChats &&
740
- currentState.groupChats.length > 0) {
741
- const groupChat = currentState.groupChats[Math.floor(this.rng.next() * currentState.groupChats.length)];
742
- if (groupChat && groupChat.memberIds.length < 10) {
743
- groupChat.invitedAgent = true;
744
- events.push({
745
- type: "group:invite",
746
- timestamp: tickTimestamp,
747
- data: {
748
- groupId: groupChat.id,
749
- groupName: groupChat.name,
750
- inviterId: groupChat.memberIds[0] ?? "unknown",
751
- },
752
- });
753
- }
754
- }
755
- // Update current state
756
- currentState.tick = i + 1;
757
- currentState.timestamp = tickTimestamp;
758
- // Update group chats array from map
759
- currentState.groupChats = Array.from(groupChatMap.values());
760
- // Create snapshot of state (shallow copy is sufficient since we're not mutating nested objects)
761
- const stateSnapshot = {
762
- ...currentState,
763
- predictionMarkets: [...currentState.predictionMarkets],
764
- perpetualMarkets: [...currentState.perpetualMarkets],
765
- agents: [...currentState.agents],
766
- posts: currentState.posts ? [...currentState.posts] : [],
767
- groupChats: currentState.groupChats
768
- ? currentState.groupChats.map((gc) => ({
769
- ...gc,
770
- memberIds: [...gc.memberIds],
771
- messages: gc.messages ? [...gc.messages] : undefined,
772
- }))
773
- : [],
774
- };
775
- ticks.push({
776
- number: i,
777
- timestamp: tickTimestamp,
778
- events,
779
- state: stateSnapshot,
780
- });
781
- }
782
- return ticks;
783
- }
784
- }
785
- /**
786
- * Seeded random number generator for reproducibility
787
- * Exported for use by other components (e.g., MarketMoverAgent)
788
- */
789
- export class SeededRandom {
790
- seed;
791
- constructor(seed) {
792
- this.seed = seed;
793
- }
794
- /**
795
- * Generate next random number (0-1)
796
- */
797
- next() {
798
- // Linear congruential generator
799
- this.seed = (this.seed * 1664525 + 1013904223) % 4294967296;
800
- return this.seed / 4294967296;
801
- }
802
- /**
803
- * Generate a random integer in the range [min, max] (inclusive)
804
- */
805
- nextInt(min, max) {
806
- return Math.floor(this.next() * (max - min + 1)) + min;
807
- }
808
- /**
809
- * Generate a random float in the range [min, max]
810
- */
811
- nextFloat(min, max) {
812
- return min + this.next() * (max - min);
813
- }
814
- /**
815
- * Pick a random element from an array
816
- */
817
- pick(array) {
818
- const index = Math.floor(this.next() * array.length);
819
- const item = array[index];
820
- if (item === undefined) {
821
- throw new Error(`Index ${index} out of bounds for array length ${array.length}`);
822
- }
823
- return item;
824
- }
825
- }