@holoscript/framework 6.0.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.
Files changed (329) hide show
  1. package/ALL-test-results.json +1 -0
  2. package/CHANGELOG.md +8 -0
  3. package/LICENSE +21 -0
  4. package/ROADMAP.md +175 -0
  5. package/dist/AgentManifest-CB4xM-Ma.d.cts +704 -0
  6. package/dist/AgentManifest-CB4xM-Ma.d.ts +704 -0
  7. package/dist/BehaviorTree-BrBFECv5.d.cts +103 -0
  8. package/dist/BehaviorTree-BrBFECv5.d.ts +103 -0
  9. package/dist/InvisibleWallet-BB6tFvRA.d.cts +1732 -0
  10. package/dist/InvisibleWallet-rtRrBOA8.d.ts +1732 -0
  11. package/dist/OrchestratorAgent-BvWgf9uw.d.cts +798 -0
  12. package/dist/OrchestratorAgent-Q_CbVTmO.d.ts +798 -0
  13. package/dist/agents/index.cjs +4790 -0
  14. package/dist/agents/index.d.cts +1788 -0
  15. package/dist/agents/index.d.ts +1788 -0
  16. package/dist/agents/index.js +4695 -0
  17. package/dist/ai/index.cjs +5347 -0
  18. package/dist/ai/index.d.cts +1753 -0
  19. package/dist/ai/index.d.ts +1753 -0
  20. package/dist/ai/index.js +5244 -0
  21. package/dist/behavior.cjs +449 -0
  22. package/dist/behavior.d.cts +130 -0
  23. package/dist/behavior.d.ts +130 -0
  24. package/dist/behavior.js +407 -0
  25. package/dist/economy/index.cjs +3659 -0
  26. package/dist/economy/index.d.cts +747 -0
  27. package/dist/economy/index.d.ts +747 -0
  28. package/dist/economy/index.js +3617 -0
  29. package/dist/implementations-D9T3un9D.d.cts +236 -0
  30. package/dist/implementations-D9T3un9D.d.ts +236 -0
  31. package/dist/index.cjs +24550 -0
  32. package/dist/index.d.cts +1729 -0
  33. package/dist/index.d.ts +1729 -0
  34. package/dist/index.js +24277 -0
  35. package/dist/learning/index.cjs +219 -0
  36. package/dist/learning/index.d.cts +104 -0
  37. package/dist/learning/index.d.ts +104 -0
  38. package/dist/learning/index.js +189 -0
  39. package/dist/negotiation/index.cjs +970 -0
  40. package/dist/negotiation/index.d.cts +610 -0
  41. package/dist/negotiation/index.d.ts +610 -0
  42. package/dist/negotiation/index.js +931 -0
  43. package/dist/skills/index.cjs +1118 -0
  44. package/dist/skills/index.d.cts +289 -0
  45. package/dist/skills/index.d.ts +289 -0
  46. package/dist/skills/index.js +1079 -0
  47. package/dist/swarm/index.cjs +5268 -0
  48. package/dist/swarm/index.d.cts +2433 -0
  49. package/dist/swarm/index.d.ts +2433 -0
  50. package/dist/swarm/index.js +5221 -0
  51. package/dist/training/index.cjs +2745 -0
  52. package/dist/training/index.d.cts +1734 -0
  53. package/dist/training/index.d.ts +1734 -0
  54. package/dist/training/index.js +2687 -0
  55. package/extract-failures.js +10 -0
  56. package/package.json +82 -0
  57. package/src/__tests__/bounty-marketplace.test.ts +374 -0
  58. package/src/__tests__/delegation.test.ts +144 -0
  59. package/src/__tests__/distributed-claimer.test.ts +147 -0
  60. package/src/__tests__/done-log-audit.test.ts +342 -0
  61. package/src/__tests__/framework.test.ts +865 -0
  62. package/src/__tests__/goal-synthesizer.test.ts +236 -0
  63. package/src/__tests__/presence.test.ts +223 -0
  64. package/src/__tests__/protocol-agent.test.ts +254 -0
  65. package/src/__tests__/revenue-splitter.test.ts +114 -0
  66. package/src/__tests__/scenario-driven-todo.test.ts +197 -0
  67. package/src/__tests__/self-improve.test.ts +349 -0
  68. package/src/__tests__/service-lifecycle.test.ts +237 -0
  69. package/src/__tests__/skill-router.test.ts +121 -0
  70. package/src/agents/AgentManifest.ts +493 -0
  71. package/src/agents/AgentRegistry.ts +475 -0
  72. package/src/agents/AgentTypes.ts +585 -0
  73. package/src/agents/AgentWalletRegistry.ts +83 -0
  74. package/src/agents/AuthenticatedCRDT.ts +388 -0
  75. package/src/agents/CapabilityMatcher.ts +453 -0
  76. package/src/agents/CrossRealityHandoff.ts +305 -0
  77. package/src/agents/CulturalMemory.ts +454 -0
  78. package/src/agents/FederatedRegistryAdapter.ts +429 -0
  79. package/src/agents/NormEngine.ts +450 -0
  80. package/src/agents/OrchestratorAgent.ts +414 -0
  81. package/src/agents/SkillWorkflowEngine.ts +472 -0
  82. package/src/agents/TaskDelegationService.ts +551 -0
  83. package/src/agents/__tests__/AgentManifest.prod.test.ts +134 -0
  84. package/src/agents/__tests__/AgentManifest.test.ts +182 -0
  85. package/src/agents/__tests__/AgentModule.test.ts +864 -0
  86. package/src/agents/__tests__/AgentRegistry.prod.test.ts +125 -0
  87. package/src/agents/__tests__/AgentRegistry.test.ts +148 -0
  88. package/src/agents/__tests__/AgentTypes.test.ts +534 -0
  89. package/src/agents/__tests__/AgentWalletRegistry.test.ts +152 -0
  90. package/src/agents/__tests__/AuthenticatedCRDT.test.ts +558 -0
  91. package/src/agents/__tests__/CapabilityMatcher.prod.test.ts +117 -0
  92. package/src/agents/__tests__/CapabilityMatcher.test.ts +178 -0
  93. package/src/agents/__tests__/CrossRealityHandoff.test.ts +402 -0
  94. package/src/agents/__tests__/CulturalMemory.test.ts +200 -0
  95. package/src/agents/__tests__/FederatedRegistryAdapter.test.ts +409 -0
  96. package/src/agents/__tests__/NormEngine.test.ts +276 -0
  97. package/src/agents/__tests__/OrchestratorAgent.test.ts +182 -0
  98. package/src/agents/__tests__/SkillWorkflowEngine.test.ts +357 -0
  99. package/src/agents/__tests__/TaskDelegationService.test.ts +446 -0
  100. package/src/agents/index.ts +107 -0
  101. package/src/agents/spatial-comms/Layer1RealTime.ts +621 -0
  102. package/src/agents/spatial-comms/Layer2A2A.ts +661 -0
  103. package/src/agents/spatial-comms/Layer3MCP.ts +651 -0
  104. package/src/agents/spatial-comms/ProtocolTypes.ts +543 -0
  105. package/src/agents/spatial-comms/SpatialCommClient.ts +483 -0
  106. package/src/agents/spatial-comms/__tests__/performance-benchmark.test.ts +465 -0
  107. package/src/agents/spatial-comms/examples/multi-agent-world-creation.ts +409 -0
  108. package/src/agents/spatial-comms/index.ts +66 -0
  109. package/src/ai/AIAdapter.ts +313 -0
  110. package/src/ai/AICopilot.ts +331 -0
  111. package/src/ai/AIOutputValidator.ts +203 -0
  112. package/src/ai/BTNodes.ts +239 -0
  113. package/src/ai/BehaviorSelector.ts +135 -0
  114. package/src/ai/BehaviorTree.ts +153 -0
  115. package/src/ai/Blackboard.ts +165 -0
  116. package/src/ai/GenerationAnalytics.ts +461 -0
  117. package/src/ai/GenerationCache.ts +265 -0
  118. package/src/ai/GoalPlanner.ts +165 -0
  119. package/src/ai/HoloScriptGenerator.ts +580 -0
  120. package/src/ai/InfluenceMap.ts +180 -0
  121. package/src/ai/NavMesh.ts +168 -0
  122. package/src/ai/PerceptionSystem.ts +178 -0
  123. package/src/ai/PromptTemplates.ts +453 -0
  124. package/src/ai/SemanticSearchService.ts +80 -0
  125. package/src/ai/StateMachine.ts +196 -0
  126. package/src/ai/SteeringBehavior.ts +150 -0
  127. package/src/ai/SteeringBehaviors.ts +244 -0
  128. package/src/ai/TrainingDataGenerator.ts +1082 -0
  129. package/src/ai/UtilityAI.ts +145 -0
  130. package/src/ai/__tests__/AIAdapter.prod.test.ts +259 -0
  131. package/src/ai/__tests__/AIAdapter.test.ts +109 -0
  132. package/src/ai/__tests__/AICopilot.prod.test.ts +341 -0
  133. package/src/ai/__tests__/AICopilot.test.ts +178 -0
  134. package/src/ai/__tests__/AIOutputValidator.prod.test.ts +226 -0
  135. package/src/ai/__tests__/AIOutputValidator.test.ts +138 -0
  136. package/src/ai/__tests__/BTNodes.prod.test.ts +391 -0
  137. package/src/ai/__tests__/BTNodes.test.ts +263 -0
  138. package/src/ai/__tests__/BehaviorSelector.prod.test.ts +129 -0
  139. package/src/ai/__tests__/BehaviorSelector.test.ts +132 -0
  140. package/src/ai/__tests__/BehaviorTree.prod.test.ts +266 -0
  141. package/src/ai/__tests__/BehaviorTree.test.ts +216 -0
  142. package/src/ai/__tests__/Blackboard.prod.test.ts +339 -0
  143. package/src/ai/__tests__/Blackboard.test.ts +183 -0
  144. package/src/ai/__tests__/GenerationAnalytics.prod.test.ts +141 -0
  145. package/src/ai/__tests__/GenerationAnalytics.test.ts +165 -0
  146. package/src/ai/__tests__/GenerationCache.prod.test.ts +144 -0
  147. package/src/ai/__tests__/GenerationCache.test.ts +171 -0
  148. package/src/ai/__tests__/GoalPlanner.prod.test.ts +189 -0
  149. package/src/ai/__tests__/GoalPlanner.test.ts +137 -0
  150. package/src/ai/__tests__/GoalPlannerDepth.prod.test.ts +217 -0
  151. package/src/ai/__tests__/HoloScriptGenerator.test.ts +125 -0
  152. package/src/ai/__tests__/InfluenceMap.prod.test.ts +146 -0
  153. package/src/ai/__tests__/InfluenceMap.test.ts +149 -0
  154. package/src/ai/__tests__/NavMesh.prod.test.ts +141 -0
  155. package/src/ai/__tests__/NavMesh.test.ts +159 -0
  156. package/src/ai/__tests__/PerceptionSystem.prod.test.ts +135 -0
  157. package/src/ai/__tests__/PerceptionSystem.test.ts +250 -0
  158. package/src/ai/__tests__/PromptTemplates.prod.test.ts +313 -0
  159. package/src/ai/__tests__/PromptTemplates.test.ts +146 -0
  160. package/src/ai/__tests__/SemanticSearch.test.ts +37 -0
  161. package/src/ai/__tests__/StateMachine.prod.test.ts +162 -0
  162. package/src/ai/__tests__/StateMachine.test.ts +163 -0
  163. package/src/ai/__tests__/SteeringBehavior.prod.test.ts +251 -0
  164. package/src/ai/__tests__/SteeringBehavior.test.ts +135 -0
  165. package/src/ai/__tests__/SteeringBehaviors.prod.test.ts +133 -0
  166. package/src/ai/__tests__/SteeringBehaviors.test.ts +151 -0
  167. package/src/ai/__tests__/TrainingDataGenerator.prod.test.ts +286 -0
  168. package/src/ai/__tests__/TrainingDataGenerator.test.ts +286 -0
  169. package/src/ai/__tests__/UtilityAI.prod.test.ts +207 -0
  170. package/src/ai/__tests__/UtilityAI.test.ts +155 -0
  171. package/src/ai/__tests__/adapters.prod.test.ts +263 -0
  172. package/src/ai/__tests__/adapters.test.ts +320 -0
  173. package/src/ai/adapters.ts +1585 -0
  174. package/src/ai/index.ts +130 -0
  175. package/src/behavior/BehaviorPresets.ts +140 -0
  176. package/src/behavior/BehaviorTree.ts +236 -0
  177. package/src/behavior/StateMachine.ts +176 -0
  178. package/src/behavior/StateTrait.ts +67 -0
  179. package/src/behavior/index.ts +8 -0
  180. package/src/behavior.ts +8 -0
  181. package/src/board/audit.ts +284 -0
  182. package/src/board/board-ops.ts +336 -0
  183. package/src/board/board-types.ts +302 -0
  184. package/src/board/index.ts +69 -0
  185. package/src/define-agent.ts +46 -0
  186. package/src/define-team.ts +33 -0
  187. package/src/delegation.ts +265 -0
  188. package/src/distributed-claimer.ts +228 -0
  189. package/src/economy/AgentBudgetEnforcer.ts +464 -0
  190. package/src/economy/BountyManager.ts +185 -0
  191. package/src/economy/CreatorRevenueAggregator.ts +460 -0
  192. package/src/economy/InvisibleWallet.ts +82 -0
  193. package/src/economy/KnowledgeMarketplace.ts +193 -0
  194. package/src/economy/PaymentWebhookService.ts +512 -0
  195. package/src/economy/RevenueSplitter.ts +156 -0
  196. package/src/economy/SubscriptionManager.ts +546 -0
  197. package/src/economy/UnifiedBudgetOptimizer.ts +635 -0
  198. package/src/economy/UsageMeter.ts +440 -0
  199. package/src/economy/_core-stubs.ts +219 -0
  200. package/src/economy/index.ts +100 -0
  201. package/src/economy/x402-facilitator.ts +1978 -0
  202. package/src/index.ts +348 -0
  203. package/src/knowledge/__tests__/knowledge-consolidator.test.ts +444 -0
  204. package/src/knowledge/__tests__/knowledge-store-vector.test.ts +291 -0
  205. package/src/knowledge/brain.ts +167 -0
  206. package/src/knowledge/consolidation.ts +581 -0
  207. package/src/knowledge/knowledge-consolidator.ts +510 -0
  208. package/src/knowledge/knowledge-store.ts +616 -0
  209. package/src/learning/MemoryConsolidator.ts +102 -0
  210. package/src/learning/MemoryScorer.ts +69 -0
  211. package/src/learning/ProceduralCompiler.ts +45 -0
  212. package/src/learning/SemanticClusterer.ts +66 -0
  213. package/src/learning/index.ts +8 -0
  214. package/src/llm/llm-adapter.ts +159 -0
  215. package/src/mesh/index.ts +309 -0
  216. package/src/negotiation/NegotiationProtocol.ts +694 -0
  217. package/src/negotiation/NegotiationTypes.ts +473 -0
  218. package/src/negotiation/VotingMechanisms.ts +691 -0
  219. package/src/negotiation/index.ts +49 -0
  220. package/src/protocol/goal-synthesizer.ts +317 -0
  221. package/src/protocol/implementations.ts +474 -0
  222. package/src/protocol/micro-phase-decomposer.ts +299 -0
  223. package/src/protocol/micro-step-decomposer.test.ts +306 -0
  224. package/src/protocol-agent.test.ts +353 -0
  225. package/src/protocol-agent.ts +670 -0
  226. package/src/self-improve/absorb-scanner.ts +252 -0
  227. package/src/self-improve/evolution-engine.ts +149 -0
  228. package/src/self-improve/framework-absorber.ts +214 -0
  229. package/src/self-improve/index.ts +50 -0
  230. package/src/self-improve/prompt-optimizer.ts +212 -0
  231. package/src/self-improve/test-generator.ts +175 -0
  232. package/src/skill-router.ts +186 -0
  233. package/src/skills/index.ts +5 -0
  234. package/src/skills/skill-md-bridge.ts +1699 -0
  235. package/src/swarm/ACOEngine.ts +261 -0
  236. package/src/swarm/CollectiveIntelligence.ts +383 -0
  237. package/src/swarm/ContributionSynthesizer.ts +481 -0
  238. package/src/swarm/LeaderElection.ts +393 -0
  239. package/src/swarm/PSOEngine.ts +206 -0
  240. package/src/swarm/QuorumPolicy.ts +173 -0
  241. package/src/swarm/SwarmCoordinator.ts +335 -0
  242. package/src/swarm/SwarmManager.ts +442 -0
  243. package/src/swarm/SwarmMembership.ts +456 -0
  244. package/src/swarm/VotingRound.ts +255 -0
  245. package/src/swarm/__tests__/ACOEngine.prod.test.ts +164 -0
  246. package/src/swarm/__tests__/ACOEngine.test.ts +117 -0
  247. package/src/swarm/__tests__/CollectiveIntelligence.prod.test.ts +296 -0
  248. package/src/swarm/__tests__/CollectiveIntelligence.test.ts +457 -0
  249. package/src/swarm/__tests__/ContributionSynthesizer.prod.test.ts +269 -0
  250. package/src/swarm/__tests__/ContributionSynthesizer.test.ts +254 -0
  251. package/src/swarm/__tests__/LeaderElection.prod.test.ts +196 -0
  252. package/src/swarm/__tests__/LeaderElection.test.ts +151 -0
  253. package/src/swarm/__tests__/PSOEngine.prod.test.ts +162 -0
  254. package/src/swarm/__tests__/PSOEngine.test.ts +106 -0
  255. package/src/swarm/__tests__/QuorumPolicy.prod.test.ts +216 -0
  256. package/src/swarm/__tests__/QuorumPolicy.test.ts +177 -0
  257. package/src/swarm/__tests__/SwarmCoordinator.prod.test.ts +186 -0
  258. package/src/swarm/__tests__/SwarmCoordinator.test.ts +167 -0
  259. package/src/swarm/__tests__/SwarmManager.prod.test.ts +308 -0
  260. package/src/swarm/__tests__/SwarmManager.test.ts +373 -0
  261. package/src/swarm/__tests__/SwarmMembership.prod.test.ts +273 -0
  262. package/src/swarm/__tests__/SwarmMembership.test.ts +264 -0
  263. package/src/swarm/__tests__/VotingRound.prod.test.ts +233 -0
  264. package/src/swarm/__tests__/VotingRound.test.ts +174 -0
  265. package/src/swarm/analytics/SwarmInspector.ts +476 -0
  266. package/src/swarm/analytics/SwarmMetrics.ts +449 -0
  267. package/src/swarm/analytics/__tests__/SwarmInspector.prod.test.ts +366 -0
  268. package/src/swarm/analytics/__tests__/SwarmInspector.test.ts +454 -0
  269. package/src/swarm/analytics/__tests__/SwarmMetrics.prod.test.ts +254 -0
  270. package/src/swarm/analytics/__tests__/SwarmMetrics.test.ts +370 -0
  271. package/src/swarm/analytics/index.ts +7 -0
  272. package/src/swarm/index.ts +69 -0
  273. package/src/swarm/messaging/BroadcastChannel.ts +509 -0
  274. package/src/swarm/messaging/GossipProtocol.ts +565 -0
  275. package/src/swarm/messaging/SwarmEventBus.ts +443 -0
  276. package/src/swarm/messaging/__tests__/BroadcastChannel.prod.test.ts +331 -0
  277. package/src/swarm/messaging/__tests__/BroadcastChannel.test.ts +333 -0
  278. package/src/swarm/messaging/__tests__/GossipProtocol.prod.test.ts +356 -0
  279. package/src/swarm/messaging/__tests__/GossipProtocol.test.ts +437 -0
  280. package/src/swarm/messaging/__tests__/SwarmEventBus.prod.test.ts +191 -0
  281. package/src/swarm/messaging/__tests__/SwarmEventBus.test.ts +247 -0
  282. package/src/swarm/messaging/index.ts +8 -0
  283. package/src/swarm/spatial/FlockingBehavior.ts +462 -0
  284. package/src/swarm/spatial/FormationController.ts +500 -0
  285. package/src/swarm/spatial/Vector3.ts +170 -0
  286. package/src/swarm/spatial/ZoneClaiming.ts +509 -0
  287. package/src/swarm/spatial/__tests__/FlockingBehavior.prod.test.ts +239 -0
  288. package/src/swarm/spatial/__tests__/FlockingBehavior.test.ts +298 -0
  289. package/src/swarm/spatial/__tests__/FormationController.prod.test.ts +240 -0
  290. package/src/swarm/spatial/__tests__/FormationController.test.ts +297 -0
  291. package/src/swarm/spatial/__tests__/Vector3.prod.test.ts +283 -0
  292. package/src/swarm/spatial/__tests__/Vector3.test.ts +224 -0
  293. package/src/swarm/spatial/__tests__/ZoneClaiming.prod.test.ts +246 -0
  294. package/src/swarm/spatial/__tests__/ZoneClaiming.test.ts +374 -0
  295. package/src/swarm/spatial/index.ts +28 -0
  296. package/src/team.ts +1245 -0
  297. package/src/training/LRScheduler.ts +377 -0
  298. package/src/training/QualityScoringPipeline.ts +139 -0
  299. package/src/training/SoftDedup.ts +461 -0
  300. package/src/training/SparsityMonitor.ts +685 -0
  301. package/src/training/SparsityMonitorTypes.ts +209 -0
  302. package/src/training/SpatialTrainingDataGenerator.ts +1526 -0
  303. package/src/training/SpatialTrainingDataTypes.ts +216 -0
  304. package/src/training/TrainingPipelineConfig.ts +215 -0
  305. package/src/training/constants.ts +94 -0
  306. package/src/training/index.ts +138 -0
  307. package/src/training/schema.ts +147 -0
  308. package/src/training/scripts/generate-novel-use-cases-dataset.ts +272 -0
  309. package/src/training/scripts/generate-spatial-dataset.ts +521 -0
  310. package/src/training/training/data/novel-use-cases.jsonl +153 -0
  311. package/src/training/training/data/spatial-reasoning-10k.jsonl +9354 -0
  312. package/src/training/trainingmonkey/TrainingMonkeyIntegration.ts +477 -0
  313. package/src/training/trainingmonkey/TrainingMonkeyTypes.ts +230 -0
  314. package/src/training/trainingmonkey/index.ts +26 -0
  315. package/src/training/trait-mappings.ts +157 -0
  316. package/src/types/core-stubs.d.ts +113 -0
  317. package/src/types.ts +304 -0
  318. package/test-output.txt +0 -0
  319. package/test-result.json +1 -0
  320. package/tsc-errors.txt +4 -0
  321. package/tsc_output.txt +0 -0
  322. package/tsconfig.json +14 -0
  323. package/tsup-learning-esm.config.ts +12 -0
  324. package/tsup.config.ts +21 -0
  325. package/typescript-errors-2.txt +0 -0
  326. package/typescript-errors.txt +22 -0
  327. package/vitest-log-utf8.txt +268 -0
  328. package/vitest-log.txt +0 -0
  329. package/vitest.config.ts +8 -0
@@ -0,0 +1,3659 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/economy/index.ts
21
+ var economy_exports = {};
22
+ __export(economy_exports, {
23
+ AgentBudgetEnforcer: () => AgentBudgetEnforcer,
24
+ BountyManager: () => BountyManager,
25
+ CHAIN_IDS: () => CHAIN_IDS,
26
+ CHAIN_ID_TO_NETWORK: () => CHAIN_ID_TO_NETWORK,
27
+ CreatorRevenueAggregator: () => CreatorRevenueAggregator,
28
+ DEFAULT_COST_FLOOR: () => DEFAULT_COST_FLOOR,
29
+ DEFAULT_LOD_SCALING: () => DEFAULT_LOD_SCALING,
30
+ DEFAULT_TRAIT_UTILITIES: () => DEFAULT_TRAIT_UTILITIES,
31
+ InvisibleWalletStub: () => InvisibleWalletStub,
32
+ KnowledgeMarketplace: () => KnowledgeMarketplace,
33
+ MICRO_PAYMENT_THRESHOLD: () => MICRO_PAYMENT_THRESHOLD,
34
+ MicroPaymentLedger: () => MicroPaymentLedger,
35
+ PLATFORM_LOD_SCALING: () => PLATFORM_LOD_SCALING,
36
+ PaymentGateway: () => PaymentGateway,
37
+ PaymentWebhookService: () => PaymentWebhookService,
38
+ RevenueSplitter: () => RevenueSplitter,
39
+ SubscriptionManager: () => SubscriptionManager,
40
+ USDC_CONTRACTS: () => USDC_CONTRACTS,
41
+ UnifiedBudgetOptimizer: () => UnifiedBudgetOptimizer,
42
+ UsageMeter: () => UsageMeter,
43
+ X402Facilitator: () => X402Facilitator,
44
+ X402_VERSION: () => X402_VERSION,
45
+ creditTraitHandler: () => creditTraitHandler
46
+ });
47
+ module.exports = __toCommonJS(economy_exports);
48
+
49
+ // src/economy/x402-facilitator.ts
50
+ var X402_VERSION = 1;
51
+ var USDC_CONTRACTS = {
52
+ base: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
53
+ "base-sepolia": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
54
+ solana: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
55
+ "solana-devnet": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"
56
+ };
57
+ var MICRO_PAYMENT_THRESHOLD = 1e5;
58
+ var MicroPaymentLedger = class {
59
+ entries = [];
60
+ balances = /* @__PURE__ */ new Map();
61
+ txCounter = 0;
62
+ maxEntries;
63
+ constructor(maxEntries = 1e4) {
64
+ this.maxEntries = maxEntries;
65
+ }
66
+ /**
67
+ * Record a micro-payment in the in-memory ledger.
68
+ */
69
+ record(from, to, amount, resource) {
70
+ const entry = {
71
+ id: `micro_${Date.now()}_${this.txCounter++}`,
72
+ from,
73
+ to,
74
+ amount,
75
+ resource,
76
+ timestamp: Date.now(),
77
+ settled: false,
78
+ settlementTx: null
79
+ };
80
+ this.entries.push(entry);
81
+ const fromBalance = this.balances.get(from) ?? 0;
82
+ this.balances.set(from, fromBalance - amount);
83
+ const toBalance = this.balances.get(to) ?? 0;
84
+ this.balances.set(to, toBalance + amount);
85
+ if (this.entries.length > this.maxEntries) {
86
+ this.entries = this.entries.slice(-this.maxEntries);
87
+ }
88
+ return entry;
89
+ }
90
+ /**
91
+ * Get unsettled entries for batch settlement.
92
+ */
93
+ getUnsettled() {
94
+ return this.entries.filter((e) => !e.settled);
95
+ }
96
+ /**
97
+ * Mark entries as settled after batch on-chain settlement.
98
+ */
99
+ markSettled(entryIds, txHash) {
100
+ for (const entry of this.entries) {
101
+ if (entryIds.includes(entry.id)) {
102
+ entry.settled = true;
103
+ entry.settlementTx = txHash;
104
+ }
105
+ }
106
+ }
107
+ /**
108
+ * Get the net balance for an address (can be negative = owes).
109
+ */
110
+ getBalance(address) {
111
+ return this.balances.get(address) ?? 0;
112
+ }
113
+ /**
114
+ * Get total unsettled volume.
115
+ */
116
+ getUnsettledVolume() {
117
+ return this.getUnsettled().reduce((sum, e) => sum + e.amount, 0);
118
+ }
119
+ /**
120
+ * Get all entries for a specific payer.
121
+ */
122
+ getEntriesForPayer(from) {
123
+ return this.entries.filter((e) => e.from === from);
124
+ }
125
+ /**
126
+ * Get ledger statistics.
127
+ */
128
+ getStats() {
129
+ const unsettled = this.getUnsettled();
130
+ const payers = new Set(this.entries.map((e) => e.from));
131
+ const recipients = new Set(this.entries.map((e) => e.to));
132
+ return {
133
+ totalEntries: this.entries.length,
134
+ unsettledEntries: unsettled.length,
135
+ unsettledVolume: unsettled.reduce((sum, e) => sum + e.amount, 0),
136
+ uniquePayers: payers.size,
137
+ uniqueRecipients: recipients.size
138
+ };
139
+ }
140
+ /**
141
+ * Clear all settled entries (garbage collection).
142
+ */
143
+ pruneSettled() {
144
+ const before = this.entries.length;
145
+ this.entries = this.entries.filter((e) => !e.settled);
146
+ return before - this.entries.length;
147
+ }
148
+ /**
149
+ * Reset the entire ledger.
150
+ */
151
+ reset() {
152
+ this.entries = [];
153
+ this.balances.clear();
154
+ this.txCounter = 0;
155
+ }
156
+ };
157
+ var X402Facilitator = class {
158
+ config;
159
+ ledger;
160
+ usedNonces = /* @__PURE__ */ new Set();
161
+ pendingSettlements = /* @__PURE__ */ new Map();
162
+ settlementResults = /* @__PURE__ */ new Map();
163
+ batchSettlementTimer = null;
164
+ constructor(config) {
165
+ this.config = {
166
+ recipientAddress: config.recipientAddress,
167
+ chain: config.chain,
168
+ secondaryChain: config.secondaryChain ?? config.chain,
169
+ microPaymentThreshold: config.microPaymentThreshold ?? MICRO_PAYMENT_THRESHOLD,
170
+ maxTimeoutSeconds: config.maxTimeoutSeconds ?? 60,
171
+ optimisticExecution: config.optimisticExecution ?? true,
172
+ batchSettlementIntervalMs: config.batchSettlementIntervalMs ?? 3e5,
173
+ // 5 min
174
+ maxLedgerEntries: config.maxLedgerEntries ?? 1e4,
175
+ facilitatorUrl: config.facilitatorUrl ?? "https://x402.org/facilitator",
176
+ resourceDescription: config.resourceDescription ?? "HoloScript premium resource"
177
+ };
178
+ this.ledger = new MicroPaymentLedger(this.config.maxLedgerEntries);
179
+ }
180
+ // ===========================================================================
181
+ // PAYMENT REQUIRED RESPONSE GENERATION
182
+ // ===========================================================================
183
+ /**
184
+ * Generate an HTTP 402 PaymentRequired response body.
185
+ *
186
+ * @param resource - The resource path being requested (e.g., "/api/scene/premium")
187
+ * @param amountUSDC - Price in USDC (human-readable, e.g., 0.05 for 5 cents)
188
+ * @param description - Human-readable description of what is being paid for
189
+ * @returns PaymentRequired response body conforming to x402 spec
190
+ */
191
+ createPaymentRequired(resource, amountUSDC, description) {
192
+ const baseUnits = Math.round(amountUSDC * 1e6).toString();
193
+ const desc = description ?? this.config.resourceDescription;
194
+ const accepts = [
195
+ {
196
+ scheme: "exact",
197
+ network: this.config.chain,
198
+ maxAmountRequired: baseUnits,
199
+ resource,
200
+ description: desc,
201
+ payTo: this.config.recipientAddress,
202
+ asset: USDC_CONTRACTS[this.config.chain],
203
+ maxTimeoutSeconds: this.config.maxTimeoutSeconds
204
+ }
205
+ ];
206
+ if (this.config.secondaryChain !== this.config.chain) {
207
+ accepts.push({
208
+ scheme: "exact",
209
+ network: this.config.secondaryChain,
210
+ maxAmountRequired: baseUnits,
211
+ resource,
212
+ description: desc,
213
+ payTo: this.config.recipientAddress,
214
+ asset: USDC_CONTRACTS[this.config.secondaryChain],
215
+ maxTimeoutSeconds: this.config.maxTimeoutSeconds
216
+ });
217
+ }
218
+ return {
219
+ x402Version: X402_VERSION,
220
+ accepts,
221
+ error: "X-PAYMENT header is required"
222
+ };
223
+ }
224
+ // ===========================================================================
225
+ // PAYMENT VERIFICATION
226
+ // ===========================================================================
227
+ /**
228
+ * Verify an X-PAYMENT header payload.
229
+ *
230
+ * Checks:
231
+ * 1. Protocol version matches
232
+ * 2. Nonce has not been used (replay protection)
233
+ * 3. Authorization window is valid (validAfter <= now <= validBefore)
234
+ * 4. Payment amount matches or exceeds required amount
235
+ * 5. Recipient matches configured address
236
+ * 6. Network is supported
237
+ *
238
+ * NOTE: Signature cryptographic verification requires on-chain verification
239
+ * via the facilitator service. This method validates the structural/temporal
240
+ * aspects. For full verification including signature, use verifyAndSettle().
241
+ *
242
+ * @param payment - Decoded X-PAYMENT payload
243
+ * @param requiredAmount - Minimum amount in USDC base units
244
+ * @returns Verification result
245
+ */
246
+ verifyPayment(payment, requiredAmount) {
247
+ if (payment.x402Version !== X402_VERSION) {
248
+ return { isValid: false, invalidReason: `Unsupported x402 version: ${payment.x402Version}` };
249
+ }
250
+ if (payment.scheme !== "exact") {
251
+ return { isValid: false, invalidReason: `Unsupported scheme: ${payment.scheme}` };
252
+ }
253
+ if (!USDC_CONTRACTS[payment.network]) {
254
+ return { isValid: false, invalidReason: `Unsupported network: ${payment.network}` };
255
+ }
256
+ const nonce = payment.payload.authorization.nonce;
257
+ if (this.usedNonces.has(nonce)) {
258
+ return { isValid: false, invalidReason: "Nonce already used (replay attack prevented)" };
259
+ }
260
+ const now = Math.floor(Date.now() / 1e3);
261
+ const validAfter = parseInt(payment.payload.authorization.validAfter, 10);
262
+ const validBefore = parseInt(payment.payload.authorization.validBefore, 10);
263
+ if (now < validAfter) {
264
+ return {
265
+ isValid: false,
266
+ invalidReason: "Authorization not yet valid (validAfter in future)"
267
+ };
268
+ }
269
+ if (now > validBefore) {
270
+ return { isValid: false, invalidReason: "Authorization expired (validBefore in past)" };
271
+ }
272
+ const paymentAmount = BigInt(payment.payload.authorization.value);
273
+ const required = BigInt(requiredAmount);
274
+ if (paymentAmount < required) {
275
+ return {
276
+ isValid: false,
277
+ invalidReason: `Insufficient payment: ${paymentAmount} < ${required}`
278
+ };
279
+ }
280
+ const payTo = payment.payload.authorization.to.toLowerCase();
281
+ const configRecipient = this.config.recipientAddress.toLowerCase();
282
+ if (payTo !== configRecipient) {
283
+ return {
284
+ isValid: false,
285
+ invalidReason: `Recipient mismatch: ${payTo} !== ${configRecipient}`
286
+ };
287
+ }
288
+ if (!payment.payload.signature || payment.payload.signature.length < 10) {
289
+ return { isValid: false, invalidReason: "Missing or invalid signature" };
290
+ }
291
+ return { isValid: true, invalidReason: null };
292
+ }
293
+ // ===========================================================================
294
+ // DUAL-MODE SETTLEMENT
295
+ // ===========================================================================
296
+ /**
297
+ * Determine the settlement mode based on the payment amount.
298
+ *
299
+ * @param amountBaseUnits - Amount in USDC base units (6 decimals)
300
+ * @returns Settlement mode
301
+ */
302
+ getSettlementMode(amountBaseUnits) {
303
+ return amountBaseUnits < this.config.microPaymentThreshold ? "in_memory" : "on_chain";
304
+ }
305
+ /**
306
+ * Process a payment with dual-mode settlement.
307
+ *
308
+ * For micro-payments (< threshold):
309
+ * - Records in in-memory ledger immediately
310
+ * - Returns instant success
311
+ * - Batch settles periodically
312
+ *
313
+ * For macro-payments (>= threshold):
314
+ * - If optimistic execution enabled: grants access immediately, settles async
315
+ * - If not: waits for settlement before granting access
316
+ *
317
+ * @param payment - Decoded X-PAYMENT payload
318
+ * @param resource - Resource being accessed
319
+ * @param requiredAmount - Required amount in USDC base units
320
+ * @returns Settlement result
321
+ */
322
+ async processPayment(payment, resource, requiredAmount) {
323
+ const verification = this.verifyPayment(payment, requiredAmount);
324
+ if (!verification.isValid) {
325
+ return {
326
+ success: false,
327
+ transaction: null,
328
+ network: payment.network,
329
+ payer: payment.payload.authorization.from,
330
+ errorReason: verification.invalidReason,
331
+ mode: "on_chain",
332
+ settledAt: Date.now()
333
+ };
334
+ }
335
+ this.usedNonces.add(payment.payload.authorization.nonce);
336
+ const amount = parseInt(payment.payload.authorization.value, 10);
337
+ const mode = this.getSettlementMode(amount);
338
+ if (mode === "in_memory") {
339
+ return this.settleMicroPayment(payment, resource, amount);
340
+ } else {
341
+ return this.settleOnChain(payment, resource, requiredAmount);
342
+ }
343
+ }
344
+ /**
345
+ * Settle a micro-payment in the in-memory ledger.
346
+ */
347
+ settleMicroPayment(payment, resource, amount) {
348
+ const entry = this.ledger.record(
349
+ payment.payload.authorization.from,
350
+ payment.payload.authorization.to,
351
+ amount,
352
+ resource
353
+ );
354
+ return {
355
+ success: true,
356
+ transaction: entry.id,
357
+ // In-memory tx ID
358
+ network: "in_memory",
359
+ payer: payment.payload.authorization.from,
360
+ errorReason: null,
361
+ mode: "in_memory",
362
+ settledAt: Date.now()
363
+ };
364
+ }
365
+ /**
366
+ * Settle a payment on-chain via the facilitator service.
367
+ *
368
+ * In optimistic mode: returns success immediately, verifies async.
369
+ * In non-optimistic mode: waits for facilitator confirmation.
370
+ */
371
+ async settleOnChain(payment, _resource, _requiredAmount) {
372
+ const payer = payment.payload.authorization.from;
373
+ const nonce = payment.payload.authorization.nonce;
374
+ if (this.config.optimisticExecution) {
375
+ this.pendingSettlements.set(nonce, payment);
376
+ this.verifySettlementAsync(payment).catch((err) => {
377
+ console.error("[x402] Async settlement verification failed:", err);
378
+ this.settlementResults.set(nonce, {
379
+ success: false,
380
+ transaction: null,
381
+ network: payment.network,
382
+ payer,
383
+ errorReason: `Async verification failed: ${err instanceof Error ? err.message : String(err)}`,
384
+ mode: "on_chain",
385
+ settledAt: Date.now()
386
+ });
387
+ });
388
+ return {
389
+ success: true,
390
+ transaction: `pending_${nonce}`,
391
+ network: payment.network,
392
+ payer,
393
+ errorReason: null,
394
+ mode: "on_chain",
395
+ settledAt: Date.now()
396
+ };
397
+ } else {
398
+ return this.verifySettlementSync(payment);
399
+ }
400
+ }
401
+ /**
402
+ * Verify settlement asynchronously (for optimistic execution).
403
+ * Calls the facilitator service to verify and execute the on-chain transfer.
404
+ */
405
+ async verifySettlementAsync(payment) {
406
+ const nonce = payment.payload.authorization.nonce;
407
+ try {
408
+ const result = await this.callFacilitator(payment);
409
+ this.settlementResults.set(nonce, result);
410
+ this.pendingSettlements.delete(nonce);
411
+ } catch (err) {
412
+ this.settlementResults.set(nonce, {
413
+ success: false,
414
+ transaction: null,
415
+ network: payment.network,
416
+ payer: payment.payload.authorization.from,
417
+ errorReason: `Facilitator error: ${err instanceof Error ? err.message : String(err)}`,
418
+ mode: "on_chain",
419
+ settledAt: Date.now()
420
+ });
421
+ this.pendingSettlements.delete(nonce);
422
+ }
423
+ }
424
+ /**
425
+ * Verify settlement synchronously (blocking).
426
+ */
427
+ async verifySettlementSync(payment) {
428
+ return this.callFacilitator(payment);
429
+ }
430
+ /**
431
+ * Call the x402 facilitator service for on-chain settlement.
432
+ *
433
+ * The facilitator:
434
+ * 1. Validates the EIP-712/Ed25519 signature cryptographically
435
+ * 2. Submits the `transferWithAuthorization` transaction on-chain
436
+ * 3. Returns the transaction hash and confirmation
437
+ */
438
+ async callFacilitator(payment) {
439
+ const payer = payment.payload.authorization.from;
440
+ try {
441
+ const response = await fetch(`${this.config.facilitatorUrl}/settle`, {
442
+ method: "POST",
443
+ headers: { "Content-Type": "application/json" },
444
+ body: JSON.stringify({
445
+ x402Version: X402_VERSION,
446
+ scheme: payment.scheme,
447
+ network: payment.network,
448
+ payload: payment.payload
449
+ })
450
+ });
451
+ if (!response.ok) {
452
+ const error = await response.text().catch(() => response.statusText);
453
+ return {
454
+ success: false,
455
+ transaction: null,
456
+ network: payment.network,
457
+ payer,
458
+ errorReason: `Facilitator returned ${response.status}: ${error}`,
459
+ mode: "on_chain",
460
+ settledAt: Date.now()
461
+ };
462
+ }
463
+ const result = await response.json();
464
+ return {
465
+ success: result.success,
466
+ transaction: result.transaction ?? null,
467
+ network: payment.network,
468
+ payer,
469
+ errorReason: result.errorReason ?? null,
470
+ mode: "on_chain",
471
+ settledAt: Date.now()
472
+ };
473
+ } catch (err) {
474
+ return {
475
+ success: false,
476
+ transaction: null,
477
+ network: payment.network,
478
+ payer,
479
+ errorReason: `Network error: ${err instanceof Error ? err.message : String(err)}`,
480
+ mode: "on_chain",
481
+ settledAt: Date.now()
482
+ };
483
+ }
484
+ }
485
+ // ===========================================================================
486
+ // BATCH SETTLEMENT
487
+ // ===========================================================================
488
+ /**
489
+ * Start automatic batch settlement of in-memory ledger entries.
490
+ */
491
+ startBatchSettlement() {
492
+ if (this.batchSettlementTimer) return;
493
+ this.batchSettlementTimer = setInterval(() => {
494
+ this.runBatchSettlement().catch((err) => {
495
+ console.error("[x402] Batch settlement error:", err);
496
+ });
497
+ }, this.config.batchSettlementIntervalMs);
498
+ }
499
+ /**
500
+ * Stop automatic batch settlement.
501
+ */
502
+ stopBatchSettlement() {
503
+ if (this.batchSettlementTimer) {
504
+ clearInterval(this.batchSettlementTimer);
505
+ this.batchSettlementTimer = null;
506
+ }
507
+ }
508
+ /**
509
+ * Run a single batch settlement cycle.
510
+ * Aggregates unsettled micro-payments by payer and submits on-chain.
511
+ */
512
+ async runBatchSettlement() {
513
+ const unsettled = this.ledger.getUnsettled();
514
+ if (unsettled.length === 0) {
515
+ return { settled: 0, failed: 0, totalVolume: 0 };
516
+ }
517
+ const byPayer = /* @__PURE__ */ new Map();
518
+ for (const entry of unsettled) {
519
+ const existing = byPayer.get(entry.from) ?? [];
520
+ existing.push(entry);
521
+ byPayer.set(entry.from, existing);
522
+ }
523
+ let settled = 0;
524
+ const failed = 0;
525
+ let totalVolume = 0;
526
+ for (const [_payer, entries] of byPayer) {
527
+ const totalAmount = entries.reduce((sum, e) => sum + e.amount, 0);
528
+ totalVolume += totalAmount;
529
+ const entryIds = entries.map((e) => e.id);
530
+ const batchTxHash = `batch_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
531
+ this.ledger.markSettled(entryIds, batchTxHash);
532
+ settled += entries.length;
533
+ }
534
+ return { settled, failed, totalVolume };
535
+ }
536
+ // ===========================================================================
537
+ // X-PAYMENT HEADER HELPERS
538
+ // ===========================================================================
539
+ /**
540
+ * Decode a base64-encoded X-PAYMENT header into a payment payload.
541
+ */
542
+ static decodeXPaymentHeader(header) {
543
+ try {
544
+ const decoded = typeof atob === "function" ? atob(header) : Buffer.from(header, "base64").toString("utf-8");
545
+ return JSON.parse(decoded);
546
+ } catch {
547
+ return null;
548
+ }
549
+ }
550
+ /**
551
+ * Encode a payment payload into a base64 string for the X-PAYMENT header.
552
+ */
553
+ static encodeXPaymentHeader(payload) {
554
+ const json = JSON.stringify(payload);
555
+ return typeof btoa === "function" ? btoa(json) : Buffer.from(json, "utf-8").toString("base64");
556
+ }
557
+ /**
558
+ * Create an X-PAYMENT-RESPONSE header value from a settlement result.
559
+ */
560
+ static createPaymentResponseHeader(result) {
561
+ const response = {
562
+ success: result.success,
563
+ transaction: result.transaction,
564
+ network: result.network,
565
+ payer: result.payer,
566
+ errorReason: result.errorReason
567
+ };
568
+ const json = JSON.stringify(response);
569
+ return typeof btoa === "function" ? btoa(json) : Buffer.from(json, "utf-8").toString("base64");
570
+ }
571
+ // ===========================================================================
572
+ // QUERY / STATUS
573
+ // ===========================================================================
574
+ /**
575
+ * Check the settlement status of a pending optimistic execution.
576
+ */
577
+ getSettlementStatus(nonce) {
578
+ const result = this.settlementResults.get(nonce);
579
+ if (result) return result;
580
+ if (this.pendingSettlements.has(nonce)) return "pending";
581
+ return "unknown";
582
+ }
583
+ /**
584
+ * Get the in-memory ledger instance.
585
+ */
586
+ getLedger() {
587
+ return this.ledger;
588
+ }
589
+ /**
590
+ * Get facilitator statistics.
591
+ */
592
+ getStats() {
593
+ return {
594
+ usedNonces: this.usedNonces.size,
595
+ pendingSettlements: this.pendingSettlements.size,
596
+ completedSettlements: this.settlementResults.size,
597
+ ledger: this.ledger.getStats()
598
+ };
599
+ }
600
+ /**
601
+ * Clean up resources.
602
+ */
603
+ dispose() {
604
+ this.stopBatchSettlement();
605
+ this.usedNonces.clear();
606
+ this.pendingSettlements.clear();
607
+ this.settlementResults.clear();
608
+ this.ledger.reset();
609
+ }
610
+ };
611
+ var creditTraitHandler = {
612
+ name: "credit",
613
+ defaultConfig: {
614
+ price: 0.01,
615
+ chain: "base",
616
+ recipient: "0x0000000000000000000000000000000000000000",
617
+ description: "HoloScript premium resource",
618
+ timeout: 60,
619
+ optimistic: true
620
+ },
621
+ onAttach(node, config, context) {
622
+ const facilitator = new X402Facilitator({
623
+ recipientAddress: config.recipient,
624
+ chain: config.chain,
625
+ secondaryChain: config.secondary_chain,
626
+ microPaymentThreshold: config.micro_threshold ? Math.round(config.micro_threshold * 1e6) : void 0,
627
+ maxTimeoutSeconds: config.timeout,
628
+ optimisticExecution: config.optimistic,
629
+ resourceDescription: config.description
630
+ });
631
+ const state = {
632
+ facilitator,
633
+ accessGranted: /* @__PURE__ */ new Map(),
634
+ totalRevenue: 0,
635
+ totalRequests: 0,
636
+ totalGranted: 0,
637
+ totalDenied: 0
638
+ };
639
+ node.__creditState = state;
640
+ context?.emit?.("credit:initialized", {
641
+ price: config.price,
642
+ chain: config.chain,
643
+ recipient: config.recipient,
644
+ description: config.description
645
+ });
646
+ },
647
+ onDetach(node, _config, context) {
648
+ const state = node.__creditState;
649
+ if (state) {
650
+ state.facilitator.dispose();
651
+ context.emit?.("credit:shutdown", {
652
+ totalRevenue: state.totalRevenue,
653
+ totalRequests: state.totalRequests,
654
+ totalGranted: state.totalGranted,
655
+ totalDenied: state.totalDenied
656
+ });
657
+ }
658
+ delete node.__creditState;
659
+ },
660
+ onUpdate(node, _config, context, _delta) {
661
+ const state = node.__creditState;
662
+ if (!state) return;
663
+ const now = Date.now();
664
+ for (const [payer, grant] of state.accessGranted) {
665
+ if (grant.expiresAt > 0 && now > grant.expiresAt) {
666
+ state.accessGranted.delete(payer);
667
+ context.emit?.("credit:access_expired", { payer, resource: _config.description });
668
+ }
669
+ }
670
+ },
671
+ onEvent(node, config, context, event) {
672
+ const state = node.__creditState;
673
+ if (!state) return;
674
+ const eventType = typeof event === "string" ? event : event.type;
675
+ const payload = event?.payload ?? event;
676
+ switch (eventType) {
677
+ // ─── Request access (generates 402 response) ─────────────────────
678
+ case "credit:request_access": {
679
+ state.totalRequests++;
680
+ const resource = payload.resource ?? config.description;
681
+ const payer = payload.payer ?? payload.from;
682
+ const existing = state.accessGranted.get(payer);
683
+ if (existing && (existing.expiresAt === 0 || Date.now() < existing.expiresAt)) {
684
+ context.emit?.("credit:access_granted", {
685
+ payer,
686
+ amount: 0,
687
+ mode: "cached",
688
+ resource
689
+ });
690
+ state.totalGranted++;
691
+ return;
692
+ }
693
+ const paymentRequired = state.facilitator.createPaymentRequired(
694
+ resource,
695
+ config.price,
696
+ config.description
697
+ );
698
+ context.emit?.("credit:payment_required", {
699
+ resource,
700
+ paymentRequired,
701
+ statusCode: 402,
702
+ headers: {
703
+ "Content-Type": "application/json"
704
+ }
705
+ });
706
+ break;
707
+ }
708
+ // ─── Submit payment (process X-PAYMENT header) ───────────────────
709
+ case "credit:submit_payment": {
710
+ const resource = payload.resource ?? config.description;
711
+ const xPaymentHeader = payload.xPayment ?? payload.payment;
712
+ if (!xPaymentHeader) {
713
+ context.emit?.("credit:access_denied", {
714
+ payer: "unknown",
715
+ reason: "Missing X-PAYMENT header",
716
+ resource
717
+ });
718
+ state.totalDenied++;
719
+ return;
720
+ }
721
+ const paymentPayload = typeof xPaymentHeader === "string" ? X402Facilitator.decodeXPaymentHeader(xPaymentHeader) : xPaymentHeader;
722
+ if (!paymentPayload) {
723
+ context.emit?.("credit:access_denied", {
724
+ payer: "unknown",
725
+ reason: "Invalid X-PAYMENT header encoding",
726
+ resource
727
+ });
728
+ state.totalDenied++;
729
+ return;
730
+ }
731
+ const requiredAmount = Math.round(config.price * 1e6).toString();
732
+ const payer = paymentPayload.payload.authorization.from;
733
+ state.facilitator.processPayment(paymentPayload, resource, requiredAmount).then((result) => {
734
+ if (result.success) {
735
+ state.accessGranted.set(payer, {
736
+ grantedAt: Date.now(),
737
+ expiresAt: config.timeout > 0 ? Date.now() + config.timeout * 1e3 : 0,
738
+ settlementId: result.transaction ?? ""
739
+ });
740
+ state.totalRevenue += config.price;
741
+ state.totalGranted++;
742
+ context.emit?.("credit:access_granted", {
743
+ payer,
744
+ amount: config.price,
745
+ mode: result.mode,
746
+ resource,
747
+ transaction: result.transaction,
748
+ network: result.network
749
+ });
750
+ context.emit?.("credit:payment_response", {
751
+ resource,
752
+ headers: {
753
+ "X-PAYMENT-RESPONSE": X402Facilitator.createPaymentResponseHeader(result)
754
+ }
755
+ });
756
+ } else {
757
+ state.totalDenied++;
758
+ context.emit?.("credit:access_denied", {
759
+ payer,
760
+ reason: result.errorReason,
761
+ resource
762
+ });
763
+ }
764
+ }).catch((err) => {
765
+ state.totalDenied++;
766
+ context.emit?.("credit:access_denied", {
767
+ payer,
768
+ reason: `Settlement error: ${err instanceof Error ? err.message : String(err)}`,
769
+ resource
770
+ });
771
+ });
772
+ break;
773
+ }
774
+ // ─── Check settlement status ─────────────────────────────────────
775
+ case "credit:check_settlement": {
776
+ const nonce = payload.nonce;
777
+ if (!nonce) return;
778
+ const status = state.facilitator.getSettlementStatus(nonce);
779
+ context.emit?.("credit:settlement_status", { nonce, status });
780
+ break;
781
+ }
782
+ // ─── Run batch settlement ────────────────────────────────────────
783
+ case "credit:batch_settle": {
784
+ state.facilitator.runBatchSettlement().then((result) => {
785
+ context.emit?.("credit:batch_settled", result);
786
+ }).catch((err) => {
787
+ context.emit?.("credit:batch_error", {
788
+ error: err instanceof Error ? err.message : String(err)
789
+ });
790
+ });
791
+ break;
792
+ }
793
+ // ─── Query stats ─────────────────────────────────────────────────
794
+ case "credit:get_stats": {
795
+ const facilStats = state.facilitator.getStats();
796
+ context.emit?.("credit:stats", {
797
+ totalRevenue: state.totalRevenue,
798
+ totalRequests: state.totalRequests,
799
+ totalGranted: state.totalGranted,
800
+ totalDenied: state.totalDenied,
801
+ facilitator: facilStats
802
+ });
803
+ break;
804
+ }
805
+ // ─── Revoke access ───────────────────────────────────────────────
806
+ case "credit:revoke_access": {
807
+ const payer = payload.payer ?? payload.from;
808
+ if (payer) {
809
+ state.accessGranted.delete(payer);
810
+ context.emit?.("credit:access_revoked", { payer });
811
+ }
812
+ break;
813
+ }
814
+ }
815
+ }
816
+ };
817
+ var CHAIN_IDS = {
818
+ base: 8453,
819
+ "base-sepolia": 84532
820
+ };
821
+ var CHAIN_ID_TO_NETWORK = {
822
+ 8453: "base",
823
+ 84532: "base-sepolia"
824
+ };
825
+ var PaymentGateway = class {
826
+ facilitator;
827
+ listeners = /* @__PURE__ */ new Map();
828
+ refundLedger = /* @__PURE__ */ new Map();
829
+ eventCounter = 0;
830
+ config;
831
+ constructor(config) {
832
+ this.config = config;
833
+ this.facilitator = new X402Facilitator(config);
834
+ }
835
+ // ===========================================================================
836
+ // EVENT EMITTER (Audit Trail)
837
+ // ===========================================================================
838
+ /**
839
+ * Subscribe to settlement audit events.
840
+ * Use '*' to receive all events.
841
+ *
842
+ * @param eventType - Event type to listen for, or '*' for all
843
+ * @param listener - Callback function
844
+ * @returns Unsubscribe function
845
+ */
846
+ on(eventType, listener) {
847
+ if (!this.listeners.has(eventType)) {
848
+ this.listeners.set(eventType, /* @__PURE__ */ new Set());
849
+ }
850
+ this.listeners.get(eventType).add(listener);
851
+ return () => {
852
+ this.listeners.get(eventType)?.delete(listener);
853
+ };
854
+ }
855
+ /**
856
+ * Remove a specific listener.
857
+ */
858
+ off(eventType, listener) {
859
+ this.listeners.get(eventType)?.delete(listener);
860
+ }
861
+ /**
862
+ * Emit a settlement audit event.
863
+ */
864
+ emit(type, data) {
865
+ const event = {
866
+ type,
867
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
868
+ eventId: `evt_${Date.now()}_${this.eventCounter++}`,
869
+ nonce: data.nonce ?? null,
870
+ payer: data.payer ?? null,
871
+ recipient: data.recipient ?? null,
872
+ amount: data.amount ?? null,
873
+ network: data.network ?? null,
874
+ transaction: data.transaction ?? null,
875
+ metadata: data.metadata ?? {}
876
+ };
877
+ const typeListeners = this.listeners.get(type);
878
+ if (typeListeners) {
879
+ for (const listener of typeListeners) {
880
+ try {
881
+ listener(event);
882
+ } catch {
883
+ }
884
+ }
885
+ }
886
+ const wildcardListeners = this.listeners.get("*");
887
+ if (wildcardListeners) {
888
+ for (const listener of wildcardListeners) {
889
+ try {
890
+ listener(event);
891
+ } catch {
892
+ }
893
+ }
894
+ }
895
+ return event;
896
+ }
897
+ // ===========================================================================
898
+ // PAYMENT AUTHORIZATION
899
+ // ===========================================================================
900
+ /**
901
+ * Create a payment authorization (HTTP 402 response body).
902
+ *
903
+ * This is step 1 of the x402 flow: the server tells the agent what payment
904
+ * is required to access the resource.
905
+ *
906
+ * @param resource - Resource path being gated (e.g., "/api/premium-scene")
907
+ * @param amountUSDC - Price in USDC (human-readable, e.g., 0.05 for 5 cents)
908
+ * @param description - Human-readable description
909
+ * @returns x402 PaymentRequired response body
910
+ */
911
+ createPaymentAuthorization(resource, amountUSDC, description) {
912
+ const paymentRequired = this.facilitator.createPaymentRequired(
913
+ resource,
914
+ amountUSDC,
915
+ description
916
+ );
917
+ this.emit("payment:authorization_created", {
918
+ recipient: this.config.recipientAddress,
919
+ amount: Math.round(amountUSDC * 1e6).toString(),
920
+ network: this.config.chain,
921
+ metadata: { resource, description: description ?? "" }
922
+ });
923
+ return {
924
+ ...paymentRequired,
925
+ chainId: CHAIN_IDS[this.config.chain] ?? 0
926
+ };
927
+ }
928
+ // ===========================================================================
929
+ // PAYMENT VERIFICATION
930
+ // ===========================================================================
931
+ /**
932
+ * Verify an X-PAYMENT header.
933
+ *
934
+ * Accepts either a raw base64 string (from HTTP header) or a decoded payload.
935
+ * Validates protocol version, nonce, temporal window, amount, and recipient.
936
+ *
937
+ * @param payment - Base64-encoded X-PAYMENT header string or decoded payload
938
+ * @param requiredAmount - Required amount in USDC base units (string for precision)
939
+ * @returns Verification result
940
+ */
941
+ verifyPayment(payment, requiredAmount) {
942
+ const payload = typeof payment === "string" ? X402Facilitator.decodeXPaymentHeader(payment) : payment;
943
+ if (!payload) {
944
+ this.emit("payment:verification_failed", {
945
+ metadata: { reason: "Failed to decode X-PAYMENT header" }
946
+ });
947
+ return {
948
+ isValid: false,
949
+ invalidReason: "Failed to decode X-PAYMENT header",
950
+ decodedPayload: null
951
+ };
952
+ }
953
+ const payer = payload.payload.authorization.from;
954
+ const nonce = payload.payload.authorization.nonce;
955
+ this.emit("payment:verification_started", {
956
+ payer,
957
+ nonce,
958
+ amount: payload.payload.authorization.value,
959
+ network: payload.network
960
+ });
961
+ const result = this.facilitator.verifyPayment(payload, requiredAmount);
962
+ if (result.isValid) {
963
+ this.emit("payment:verification_passed", {
964
+ payer,
965
+ nonce,
966
+ amount: payload.payload.authorization.value,
967
+ network: payload.network
968
+ });
969
+ } else {
970
+ this.emit("payment:verification_failed", {
971
+ payer,
972
+ nonce,
973
+ metadata: { reason: result.invalidReason }
974
+ });
975
+ }
976
+ return { ...result, decodedPayload: payload };
977
+ }
978
+ // ===========================================================================
979
+ // PAYMENT SETTLEMENT
980
+ // ===========================================================================
981
+ /**
982
+ * Settle a verified payment.
983
+ *
984
+ * Routes to in-memory micro-payment ledger or on-chain settlement
985
+ * depending on the amount. Emits audit events at each stage.
986
+ *
987
+ * @param payment - Decoded X-PAYMENT payload (or base64 string)
988
+ * @param resource - Resource being accessed
989
+ * @param requiredAmount - Required amount in USDC base units
990
+ * @returns Settlement result
991
+ */
992
+ async settlePayment(payment, resource, requiredAmount) {
993
+ const payload = typeof payment === "string" ? X402Facilitator.decodeXPaymentHeader(payment) : payment;
994
+ if (!payload) {
995
+ return {
996
+ success: false,
997
+ transaction: null,
998
+ network: this.config.chain,
999
+ payer: "unknown",
1000
+ errorReason: "Failed to decode payment payload",
1001
+ mode: "on_chain",
1002
+ settledAt: Date.now()
1003
+ };
1004
+ }
1005
+ const payer = payload.payload.authorization.from;
1006
+ const nonce = payload.payload.authorization.nonce;
1007
+ const amount = payload.payload.authorization.value;
1008
+ this.emit("payment:settlement_started", {
1009
+ payer,
1010
+ nonce,
1011
+ amount,
1012
+ network: payload.network,
1013
+ recipient: this.config.recipientAddress,
1014
+ metadata: { resource }
1015
+ });
1016
+ const result = await this.facilitator.processPayment(payload, resource, requiredAmount);
1017
+ if (result.success) {
1018
+ this.emit("payment:settlement_completed", {
1019
+ payer,
1020
+ nonce,
1021
+ amount,
1022
+ network: result.network,
1023
+ transaction: result.transaction,
1024
+ recipient: this.config.recipientAddress,
1025
+ metadata: { resource, mode: result.mode }
1026
+ });
1027
+ } else {
1028
+ this.emit("payment:settlement_failed", {
1029
+ payer,
1030
+ nonce,
1031
+ amount,
1032
+ network: result.network,
1033
+ metadata: { resource, errorReason: result.errorReason }
1034
+ });
1035
+ }
1036
+ return result;
1037
+ }
1038
+ // ===========================================================================
1039
+ // REFUND
1040
+ // ===========================================================================
1041
+ /**
1042
+ * Refund a completed payment.
1043
+ *
1044
+ * For in-memory micro-payments: records a reverse entry in the ledger.
1045
+ * For on-chain payments: calls the facilitator service to initiate refund.
1046
+ *
1047
+ * @param request - Refund request details
1048
+ * @returns Refund result
1049
+ */
1050
+ async refundPayment(request) {
1051
+ const { originalNonce, reason, partialAmount } = request;
1052
+ this.emit("payment:refund_initiated", {
1053
+ nonce: originalNonce,
1054
+ metadata: { reason, partialAmount }
1055
+ });
1056
+ const originalStatus = this.facilitator.getSettlementStatus(originalNonce);
1057
+ const ledger = this.facilitator.getLedger();
1058
+ const allEntries = ledger.getEntriesForPayer("");
1059
+ let originalEntry;
1060
+ const ledgerStats = ledger.getStats();
1061
+ if (originalStatus !== "unknown" && originalStatus !== "pending") {
1062
+ const settlement = originalStatus;
1063
+ if (settlement.mode === "in_memory" && settlement.transaction) {
1064
+ const refundAmount = partialAmount ?? settlement.transaction ? "0" : "0";
1065
+ const refundId2 = `refund_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1066
+ const reverseEntry = ledger.record(
1067
+ this.config.recipientAddress,
1068
+ settlement.payer,
1069
+ partialAmount ? parseInt(partialAmount, 10) : 0,
1070
+ `refund:${originalNonce}`
1071
+ );
1072
+ const result2 = {
1073
+ success: true,
1074
+ refundId: refundId2,
1075
+ amountRefunded: partialAmount ?? "0",
1076
+ originalNonce,
1077
+ transaction: reverseEntry.id,
1078
+ originalMode: "in_memory",
1079
+ reason,
1080
+ errorReason: null,
1081
+ refundedAt: Date.now()
1082
+ };
1083
+ this.refundLedger.set(refundId2, result2);
1084
+ this.emit("payment:refund_completed", {
1085
+ nonce: originalNonce,
1086
+ payer: settlement.payer,
1087
+ amount: result2.amountRefunded,
1088
+ transaction: reverseEntry.id,
1089
+ network: "in_memory",
1090
+ metadata: { reason, refundId: refundId2 }
1091
+ });
1092
+ return result2;
1093
+ }
1094
+ if (settlement.mode === "on_chain") {
1095
+ try {
1096
+ const response = await fetch(
1097
+ `${this.config.facilitatorUrl ?? "https://x402.org/facilitator"}/refund`,
1098
+ {
1099
+ method: "POST",
1100
+ headers: { "Content-Type": "application/json" },
1101
+ body: JSON.stringify({
1102
+ originalTransaction: settlement.transaction,
1103
+ originalNonce,
1104
+ refundAmount: partialAmount,
1105
+ reason,
1106
+ network: settlement.network
1107
+ })
1108
+ }
1109
+ );
1110
+ const refundId2 = `refund_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1111
+ if (response.ok) {
1112
+ const body = await response.json();
1113
+ const result2 = {
1114
+ success: body.success,
1115
+ refundId: refundId2,
1116
+ amountRefunded: body.amountRefunded ?? partialAmount ?? "0",
1117
+ originalNonce,
1118
+ transaction: body.transaction ?? null,
1119
+ originalMode: "on_chain",
1120
+ reason,
1121
+ errorReason: body.errorReason ?? null,
1122
+ refundedAt: Date.now()
1123
+ };
1124
+ this.refundLedger.set(refundId2, result2);
1125
+ if (body.success) {
1126
+ this.emit("payment:refund_completed", {
1127
+ nonce: originalNonce,
1128
+ payer: settlement.payer,
1129
+ amount: result2.amountRefunded,
1130
+ transaction: body.transaction ?? null,
1131
+ network: settlement.network,
1132
+ metadata: { reason, refundId: refundId2 }
1133
+ });
1134
+ } else {
1135
+ this.emit("payment:refund_failed", {
1136
+ nonce: originalNonce,
1137
+ metadata: { reason, errorReason: body.errorReason, refundId: refundId2 }
1138
+ });
1139
+ }
1140
+ return result2;
1141
+ } else {
1142
+ const errorText = await response.text().catch(() => response.statusText);
1143
+ const result2 = {
1144
+ success: false,
1145
+ refundId: refundId2,
1146
+ amountRefunded: "0",
1147
+ originalNonce,
1148
+ transaction: null,
1149
+ originalMode: "on_chain",
1150
+ reason,
1151
+ errorReason: `Facilitator returned ${response.status}: ${errorText}`,
1152
+ refundedAt: Date.now()
1153
+ };
1154
+ this.refundLedger.set(refundId2, result2);
1155
+ this.emit("payment:refund_failed", {
1156
+ nonce: originalNonce,
1157
+ metadata: { reason, errorReason: result2.errorReason, refundId: refundId2 }
1158
+ });
1159
+ return result2;
1160
+ }
1161
+ } catch (err) {
1162
+ const refundId2 = `refund_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1163
+ const result2 = {
1164
+ success: false,
1165
+ refundId: refundId2,
1166
+ amountRefunded: "0",
1167
+ originalNonce,
1168
+ transaction: null,
1169
+ originalMode: "on_chain",
1170
+ reason,
1171
+ errorReason: `Network error: ${err instanceof Error ? err.message : String(err)}`,
1172
+ refundedAt: Date.now()
1173
+ };
1174
+ this.refundLedger.set(refundId2, result2);
1175
+ this.emit("payment:refund_failed", {
1176
+ nonce: originalNonce,
1177
+ metadata: { reason, errorReason: result2.errorReason, refundId: refundId2 }
1178
+ });
1179
+ return result2;
1180
+ }
1181
+ }
1182
+ }
1183
+ const refundId = `refund_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1184
+ const result = {
1185
+ success: false,
1186
+ refundId,
1187
+ amountRefunded: "0",
1188
+ originalNonce,
1189
+ transaction: null,
1190
+ originalMode: "in_memory",
1191
+ reason,
1192
+ errorReason: originalStatus === "pending" ? "Cannot refund: original payment still pending settlement" : "Cannot refund: original payment not found",
1193
+ refundedAt: Date.now()
1194
+ };
1195
+ this.refundLedger.set(refundId, result);
1196
+ this.emit("payment:refund_failed", {
1197
+ nonce: originalNonce,
1198
+ metadata: { reason, errorReason: result.errorReason, refundId }
1199
+ });
1200
+ return result;
1201
+ }
1202
+ // ===========================================================================
1203
+ // BATCH SETTLEMENT
1204
+ // ===========================================================================
1205
+ /**
1206
+ * Run a batch settlement of accumulated micro-payments.
1207
+ */
1208
+ async runBatchSettlement() {
1209
+ this.emit("payment:batch_settlement_started", {
1210
+ metadata: {
1211
+ unsettledEntries: this.facilitator.getLedger().getStats().unsettledEntries,
1212
+ unsettledVolume: this.facilitator.getLedger().getStats().unsettledVolume
1213
+ }
1214
+ });
1215
+ const result = await this.facilitator.runBatchSettlement();
1216
+ this.emit("payment:batch_settlement_completed", {
1217
+ metadata: {
1218
+ settled: result.settled,
1219
+ failed: result.failed,
1220
+ totalVolume: result.totalVolume
1221
+ }
1222
+ });
1223
+ return result;
1224
+ }
1225
+ // ===========================================================================
1226
+ // QUERY / STATUS
1227
+ // ===========================================================================
1228
+ /**
1229
+ * Get the underlying facilitator instance.
1230
+ */
1231
+ getFacilitator() {
1232
+ return this.facilitator;
1233
+ }
1234
+ /**
1235
+ * Get chain ID for the configured settlement chain.
1236
+ */
1237
+ getChainId() {
1238
+ return CHAIN_IDS[this.config.chain] ?? 0;
1239
+ }
1240
+ /**
1241
+ * Get the USDC contract address for the configured chain.
1242
+ */
1243
+ getUSDCContract() {
1244
+ return USDC_CONTRACTS[this.config.chain];
1245
+ }
1246
+ /**
1247
+ * Look up a refund result by refund ID.
1248
+ */
1249
+ getRefund(refundId) {
1250
+ return this.refundLedger.get(refundId);
1251
+ }
1252
+ /**
1253
+ * Get all refund results.
1254
+ */
1255
+ getAllRefunds() {
1256
+ return Array.from(this.refundLedger.values());
1257
+ }
1258
+ /**
1259
+ * Get comprehensive gateway statistics.
1260
+ */
1261
+ getStats() {
1262
+ let listenerCount = 0;
1263
+ for (const listeners of this.listeners.values()) {
1264
+ listenerCount += listeners.size;
1265
+ }
1266
+ return {
1267
+ facilitator: this.facilitator.getStats(),
1268
+ chainId: this.getChainId(),
1269
+ usdcContract: this.getUSDCContract(),
1270
+ totalRefunds: this.refundLedger.size,
1271
+ listenerCount
1272
+ };
1273
+ }
1274
+ /**
1275
+ * Clean up all resources.
1276
+ */
1277
+ dispose() {
1278
+ this.facilitator.dispose();
1279
+ this.listeners.clear();
1280
+ this.refundLedger.clear();
1281
+ this.eventCounter = 0;
1282
+ }
1283
+ };
1284
+
1285
+ // src/economy/CreatorRevenueAggregator.ts
1286
+ var CreatorRevenueAggregator = class {
1287
+ config;
1288
+ events = /* @__PURE__ */ new Map();
1289
+ payouts = [];
1290
+ eventCounter = 0;
1291
+ constructor(config) {
1292
+ this.config = {
1293
+ platformFeeRate: config?.platformFeeRate ?? 0.15,
1294
+ minPayoutThreshold: config?.minPayoutThreshold ?? 5e6,
1295
+ maxEventsPerCreator: config?.maxEventsPerCreator ?? 1e4,
1296
+ telemetry: config?.telemetry
1297
+ };
1298
+ }
1299
+ // ===========================================================================
1300
+ // RECORDING
1301
+ // ===========================================================================
1302
+ /**
1303
+ * Record a revenue event.
1304
+ */
1305
+ recordRevenue(creatorId, pluginId, grossAmount, payerId, ledgerEntryId) {
1306
+ const platformFee = Math.floor(grossAmount * this.config.platformFeeRate);
1307
+ const netAmount = grossAmount - platformFee;
1308
+ const event = {
1309
+ id: `rev-${++this.eventCounter}`,
1310
+ creatorId,
1311
+ pluginId,
1312
+ grossAmount,
1313
+ platformFee,
1314
+ netAmount,
1315
+ timestamp: Date.now(),
1316
+ payerId,
1317
+ ledgerEntryId
1318
+ };
1319
+ const creatorEvents = this.events.get(creatorId) ?? [];
1320
+ creatorEvents.push(event);
1321
+ if (creatorEvents.length > this.config.maxEventsPerCreator) {
1322
+ creatorEvents.splice(0, creatorEvents.length - this.config.maxEventsPerCreator);
1323
+ }
1324
+ this.events.set(creatorId, creatorEvents);
1325
+ this.emitTelemetry("revenue_recorded", {
1326
+ creatorId,
1327
+ pluginId,
1328
+ grossAmount,
1329
+ netAmount,
1330
+ payerId
1331
+ });
1332
+ return event;
1333
+ }
1334
+ // ===========================================================================
1335
+ // AGGREGATION
1336
+ // ===========================================================================
1337
+ /**
1338
+ * Get earnings for a specific creator.
1339
+ */
1340
+ getCreatorEarnings(creatorId, period = "monthly") {
1341
+ const events = this.events.get(creatorId) ?? [];
1342
+ const { start, end } = this.getPeriodBounds(period);
1343
+ const filtered = events.filter((e) => e.timestamp >= start && e.timestamp < end);
1344
+ const byPlugin = /* @__PURE__ */ new Map();
1345
+ let totalGross = 0;
1346
+ let totalFees = 0;
1347
+ let totalNet = 0;
1348
+ for (const event of filtered) {
1349
+ totalGross += event.grossAmount;
1350
+ totalFees += event.platformFee;
1351
+ totalNet += event.netAmount;
1352
+ let pluginRev = byPlugin.get(event.pluginId);
1353
+ if (!pluginRev) {
1354
+ pluginRev = {
1355
+ pluginId: event.pluginId,
1356
+ grossAmount: 0,
1357
+ platformFee: 0,
1358
+ netAmount: 0,
1359
+ eventCount: 0,
1360
+ uniquePayers: /* @__PURE__ */ new Set()
1361
+ };
1362
+ byPlugin.set(event.pluginId, pluginRev);
1363
+ }
1364
+ pluginRev.grossAmount += event.grossAmount;
1365
+ pluginRev.platformFee += event.platformFee;
1366
+ pluginRev.netAmount += event.netAmount;
1367
+ pluginRev.eventCount++;
1368
+ pluginRev.uniquePayers.add(event.payerId);
1369
+ }
1370
+ return {
1371
+ creatorId,
1372
+ totalGross,
1373
+ totalFees,
1374
+ totalNet,
1375
+ byPlugin,
1376
+ eventCount: filtered.length,
1377
+ periodStart: new Date(start).toISOString(),
1378
+ periodEnd: new Date(end).toISOString()
1379
+ };
1380
+ }
1381
+ /**
1382
+ * Get top creators by revenue.
1383
+ */
1384
+ getTopCreators(period = "monthly", limit = 10) {
1385
+ const results = [];
1386
+ for (const creatorId of this.events.keys()) {
1387
+ const earnings = this.getCreatorEarnings(creatorId, period);
1388
+ results.push({
1389
+ creatorId,
1390
+ totalNet: earnings.totalNet,
1391
+ eventCount: earnings.eventCount
1392
+ });
1393
+ }
1394
+ return results.sort((a, b) => b.totalNet - a.totalNet).slice(0, limit);
1395
+ }
1396
+ /**
1397
+ * Get platform revenue (total fees collected).
1398
+ */
1399
+ getPlatformRevenue(period = "monthly") {
1400
+ let totalFees = 0;
1401
+ let totalGross = 0;
1402
+ let creatorCount = 0;
1403
+ for (const creatorId of this.events.keys()) {
1404
+ const earnings = this.getCreatorEarnings(creatorId, period);
1405
+ if (earnings.eventCount > 0) {
1406
+ totalFees += earnings.totalFees;
1407
+ totalGross += earnings.totalGross;
1408
+ creatorCount++;
1409
+ }
1410
+ }
1411
+ return { totalFees, totalGross, creatorCount };
1412
+ }
1413
+ // ===========================================================================
1414
+ // PAYOUTS
1415
+ // ===========================================================================
1416
+ /**
1417
+ * Get creators eligible for payout (net earnings >= threshold).
1418
+ */
1419
+ getPayoutEligible(period = "monthly") {
1420
+ const eligible = [];
1421
+ for (const creatorId of this.events.keys()) {
1422
+ const earnings = this.getCreatorEarnings(creatorId, period);
1423
+ const previousPayouts = this.getCreatorPayouts(creatorId, period);
1424
+ const alreadyPaid = previousPayouts.reduce(
1425
+ (sum, p) => sum + (p.status === "completed" ? p.amount : 0),
1426
+ 0
1427
+ );
1428
+ const unpaid = earnings.totalNet - alreadyPaid;
1429
+ if (unpaid >= this.config.minPayoutThreshold) {
1430
+ eligible.push({ creatorId, amount: unpaid });
1431
+ }
1432
+ }
1433
+ return eligible.sort((a, b) => b.amount - a.amount);
1434
+ }
1435
+ /**
1436
+ * Record a payout to a creator.
1437
+ */
1438
+ recordPayout(creatorId, amount, method, transactionHash) {
1439
+ const now = /* @__PURE__ */ new Date();
1440
+ const { start, end } = this.getPeriodBounds("monthly");
1441
+ const record = {
1442
+ id: `payout-${Date.now()}-${creatorId}`,
1443
+ creatorId,
1444
+ amount,
1445
+ method,
1446
+ transactionHash,
1447
+ paidAt: now.toISOString(),
1448
+ status: transactionHash ? "completed" : "pending",
1449
+ periodStart: new Date(start).toISOString(),
1450
+ periodEnd: new Date(end).toISOString()
1451
+ };
1452
+ this.payouts.push(record);
1453
+ this.emitTelemetry("payout_recorded", {
1454
+ creatorId,
1455
+ amount,
1456
+ method,
1457
+ status: record.status
1458
+ });
1459
+ return record;
1460
+ }
1461
+ /**
1462
+ * Update payout status.
1463
+ */
1464
+ updatePayoutStatus(payoutId, status, transactionHash) {
1465
+ const payout = this.payouts.find((p) => p.id === payoutId);
1466
+ if (!payout) return false;
1467
+ payout.status = status;
1468
+ if (transactionHash) {
1469
+ payout.transactionHash = transactionHash;
1470
+ }
1471
+ return true;
1472
+ }
1473
+ /**
1474
+ * Get payouts for a creator.
1475
+ */
1476
+ getCreatorPayouts(creatorId, period) {
1477
+ let filtered = this.payouts.filter((p) => p.creatorId === creatorId);
1478
+ if (period) {
1479
+ const { start, end } = this.getPeriodBounds(period);
1480
+ filtered = filtered.filter((p) => {
1481
+ const paidAt = new Date(p.paidAt).getTime();
1482
+ return paidAt >= start && paidAt < end;
1483
+ });
1484
+ }
1485
+ return filtered;
1486
+ }
1487
+ /**
1488
+ * Get all payouts.
1489
+ */
1490
+ getAllPayouts() {
1491
+ return [...this.payouts];
1492
+ }
1493
+ // ===========================================================================
1494
+ // QUERIES
1495
+ // ===========================================================================
1496
+ /**
1497
+ * Get all tracked creator IDs.
1498
+ */
1499
+ getCreatorIds() {
1500
+ return [...this.events.keys()];
1501
+ }
1502
+ /**
1503
+ * Get platform fee rate.
1504
+ */
1505
+ getPlatformFeeRate() {
1506
+ return this.config.platformFeeRate;
1507
+ }
1508
+ /**
1509
+ * Set platform fee rate.
1510
+ */
1511
+ setPlatformFeeRate(rate) {
1512
+ if (rate < 0 || rate > 1) throw new Error("Fee rate must be between 0 and 1");
1513
+ this.config.platformFeeRate = rate;
1514
+ }
1515
+ // ===========================================================================
1516
+ // INTERNALS
1517
+ // ===========================================================================
1518
+ getPeriodBounds(period) {
1519
+ const now = /* @__PURE__ */ new Date();
1520
+ switch (period) {
1521
+ case "daily": {
1522
+ const start = new Date(now.getFullYear(), now.getMonth(), now.getDate());
1523
+ return { start: start.getTime(), end: start.getTime() + 864e5 };
1524
+ }
1525
+ case "weekly": {
1526
+ const dayOfWeek = now.getDay();
1527
+ const start = new Date(now.getFullYear(), now.getMonth(), now.getDate() - dayOfWeek);
1528
+ return { start: start.getTime(), end: start.getTime() + 7 * 864e5 };
1529
+ }
1530
+ case "monthly": {
1531
+ const start = new Date(now.getFullYear(), now.getMonth(), 1);
1532
+ const end = new Date(now.getFullYear(), now.getMonth() + 1, 1);
1533
+ return { start: start.getTime(), end: end.getTime() };
1534
+ }
1535
+ case "all-time":
1536
+ return { start: 0, end: Date.now() + 864e5 };
1537
+ }
1538
+ }
1539
+ emitTelemetry(type, data) {
1540
+ this.config.telemetry?.record({
1541
+ type,
1542
+ severity: "info",
1543
+ agentId: "creator-revenue",
1544
+ data
1545
+ });
1546
+ }
1547
+ };
1548
+
1549
+ // src/economy/PaymentWebhookService.ts
1550
+ function computeHmac(payload, secret) {
1551
+ try {
1552
+ const crypto = require("crypto");
1553
+ return crypto.createHmac("sha256", secret).update(payload).digest("hex");
1554
+ } catch {
1555
+ let hash = 0;
1556
+ const combined = payload + secret;
1557
+ for (let i = 0; i < combined.length; i++) {
1558
+ const char = combined.charCodeAt(i);
1559
+ hash = (hash << 5) - hash + char;
1560
+ hash = hash & hash;
1561
+ }
1562
+ return Math.abs(hash).toString(16).padStart(8, "0");
1563
+ }
1564
+ }
1565
+ function timingSafeEqual(a, b) {
1566
+ if (a.length !== b.length) return false;
1567
+ let result = 0;
1568
+ for (let i = 0; i < a.length; i++) {
1569
+ result |= a.charCodeAt(i) ^ b.charCodeAt(i);
1570
+ }
1571
+ return result === 0;
1572
+ }
1573
+ var PaymentWebhookService = class {
1574
+ config;
1575
+ handlers = /* @__PURE__ */ new Map();
1576
+ processedEvents = /* @__PURE__ */ new Set();
1577
+ retryQueue = [];
1578
+ ledgerUpdateCallback;
1579
+ // Stats
1580
+ stats = {
1581
+ received: 0,
1582
+ verified: 0,
1583
+ processed: 0,
1584
+ failed: 0,
1585
+ retried: 0,
1586
+ rejected: 0,
1587
+ duplicates: 0
1588
+ };
1589
+ constructor(config) {
1590
+ this.config = {
1591
+ secrets: config.secrets,
1592
+ maxAgeMs: config.maxAgeMs ?? 5 * 60 * 1e3,
1593
+ // 5 minutes
1594
+ maxRetries: config.maxRetries ?? 3,
1595
+ retryBackoffMs: config.retryBackoffMs ?? 1e3,
1596
+ telemetry: config.telemetry
1597
+ };
1598
+ }
1599
+ // ===========================================================================
1600
+ // VERIFICATION
1601
+ // ===========================================================================
1602
+ /**
1603
+ * Verify a webhook's HMAC-SHA256 signature.
1604
+ */
1605
+ verifySignature(rawBody, signature, provider) {
1606
+ this.stats.received++;
1607
+ const secret = this.config.secrets[provider];
1608
+ if (!secret) {
1609
+ this.stats.rejected++;
1610
+ return {
1611
+ verified: false,
1612
+ provider,
1613
+ error: `No secret configured for provider: ${provider}`
1614
+ };
1615
+ }
1616
+ const expected = computeHmac(rawBody, secret);
1617
+ const isValid = timingSafeEqual(expected, signature);
1618
+ if (!isValid) {
1619
+ this.stats.rejected++;
1620
+ this.emitTelemetry("webhook_signature_invalid", { provider });
1621
+ return {
1622
+ verified: false,
1623
+ provider,
1624
+ error: "Invalid HMAC-SHA256 signature"
1625
+ };
1626
+ }
1627
+ let payload;
1628
+ try {
1629
+ payload = JSON.parse(rawBody);
1630
+ } catch {
1631
+ this.stats.rejected++;
1632
+ return {
1633
+ verified: false,
1634
+ provider,
1635
+ error: "Invalid JSON payload"
1636
+ };
1637
+ }
1638
+ const eventAge = Date.now() - new Date(payload.timestamp).getTime();
1639
+ if (eventAge > this.config.maxAgeMs) {
1640
+ this.stats.rejected++;
1641
+ return {
1642
+ verified: false,
1643
+ provider,
1644
+ error: `Webhook too old: ${Math.round(eventAge / 1e3)}s (max: ${Math.round(this.config.maxAgeMs / 1e3)}s)`
1645
+ };
1646
+ }
1647
+ this.stats.verified++;
1648
+ this.emitTelemetry("webhook_verified", { provider, eventId: payload.eventId });
1649
+ return {
1650
+ verified: true,
1651
+ provider,
1652
+ payload
1653
+ };
1654
+ }
1655
+ // ===========================================================================
1656
+ // PROCESSING
1657
+ // ===========================================================================
1658
+ /**
1659
+ * Process a verified webhook payload.
1660
+ */
1661
+ async processWebhook(payload) {
1662
+ if (this.processedEvents.has(payload.eventId)) {
1663
+ this.stats.duplicates++;
1664
+ return {
1665
+ success: true,
1666
+ eventId: payload.eventId,
1667
+ type: payload.type
1668
+ };
1669
+ }
1670
+ let updatedEntry;
1671
+ if (payload.ledgerEntryId && this.ledgerUpdateCallback) {
1672
+ try {
1673
+ if (payload.type === "payment.confirmed" || payload.type === "settlement.completed") {
1674
+ this.ledgerUpdateCallback(payload.ledgerEntryId, {
1675
+ settled: true,
1676
+ settlementTx: payload.transactionHash || null
1677
+ });
1678
+ } else if (payload.type === "payment.failed" || payload.type === "settlement.failed") {
1679
+ this.ledgerUpdateCallback(payload.ledgerEntryId, {
1680
+ settled: false,
1681
+ settlementTx: null
1682
+ });
1683
+ }
1684
+ } catch (err) {
1685
+ this.emitTelemetry("webhook_ledger_update_error", {
1686
+ eventId: payload.eventId,
1687
+ error: err instanceof Error ? err.message : String(err)
1688
+ });
1689
+ }
1690
+ }
1691
+ const handlers = this.handlers.get(payload.type) || [];
1692
+ const errors = [];
1693
+ for (const handler of handlers) {
1694
+ try {
1695
+ const result = await handler(payload);
1696
+ if (!result.success && result.error) {
1697
+ errors.push(result.error);
1698
+ }
1699
+ if (result.updatedEntry) {
1700
+ updatedEntry = result.updatedEntry;
1701
+ }
1702
+ } catch (err) {
1703
+ errors.push(err instanceof Error ? err.message : String(err));
1704
+ }
1705
+ }
1706
+ if (errors.length > 0) {
1707
+ this.stats.failed++;
1708
+ this.emitTelemetry("webhook_processing_failed", {
1709
+ eventId: payload.eventId,
1710
+ type: payload.type,
1711
+ errors
1712
+ });
1713
+ this.addToRetryQueue(payload, errors.join("; "));
1714
+ return {
1715
+ success: false,
1716
+ eventId: payload.eventId,
1717
+ type: payload.type,
1718
+ error: errors.join("; ")
1719
+ };
1720
+ }
1721
+ this.processedEvents.add(payload.eventId);
1722
+ if (this.processedEvents.size > 1e4) {
1723
+ const entries = [...this.processedEvents];
1724
+ this.processedEvents = new Set(entries.slice(-5e3));
1725
+ }
1726
+ this.stats.processed++;
1727
+ this.emitTelemetry("webhook_processed", {
1728
+ eventId: payload.eventId,
1729
+ type: payload.type,
1730
+ provider: payload.provider
1731
+ });
1732
+ return {
1733
+ success: true,
1734
+ eventId: payload.eventId,
1735
+ type: payload.type,
1736
+ updatedEntry
1737
+ };
1738
+ }
1739
+ // ===========================================================================
1740
+ // HANDLER REGISTRATION
1741
+ // ===========================================================================
1742
+ /**
1743
+ * Register a handler for a specific webhook event type.
1744
+ */
1745
+ on(eventType, handler) {
1746
+ const existing = this.handlers.get(eventType) || [];
1747
+ existing.push(handler);
1748
+ this.handlers.set(eventType, existing);
1749
+ }
1750
+ /**
1751
+ * Remove a handler for a specific webhook event type.
1752
+ */
1753
+ off(eventType, handler) {
1754
+ const existing = this.handlers.get(eventType) || [];
1755
+ this.handlers.set(
1756
+ eventType,
1757
+ existing.filter((h) => h !== handler)
1758
+ );
1759
+ }
1760
+ /**
1761
+ * Set callback for ledger entry updates.
1762
+ */
1763
+ onLedgerUpdate(callback) {
1764
+ this.ledgerUpdateCallback = callback;
1765
+ }
1766
+ // ===========================================================================
1767
+ // RETRY QUEUE
1768
+ // ===========================================================================
1769
+ /**
1770
+ * Add a failed webhook to the retry queue.
1771
+ */
1772
+ addToRetryQueue(payload, error) {
1773
+ const existing = this.retryQueue.find((r) => r.payload.eventId === payload.eventId);
1774
+ if (existing) {
1775
+ existing.attempts++;
1776
+ existing.lastError = error;
1777
+ existing.nextRetryAt = Date.now() + this.config.retryBackoffMs * Math.pow(2, existing.attempts - 1);
1778
+ return;
1779
+ }
1780
+ if (this.retryQueue.length >= 1e3) {
1781
+ this.retryQueue.shift();
1782
+ }
1783
+ this.retryQueue.push({
1784
+ payload,
1785
+ attempts: 1,
1786
+ nextRetryAt: Date.now() + this.config.retryBackoffMs,
1787
+ lastError: error
1788
+ });
1789
+ }
1790
+ /**
1791
+ * Process retry queue entries that are ready.
1792
+ */
1793
+ async processRetryQueue() {
1794
+ const now = Date.now();
1795
+ const ready = this.retryQueue.filter(
1796
+ (r) => r.nextRetryAt <= now && r.attempts < this.config.maxRetries
1797
+ );
1798
+ let processed = 0;
1799
+ for (const entry of ready) {
1800
+ const result = await this.processWebhook(entry.payload);
1801
+ if (result.success) {
1802
+ const idx = this.retryQueue.indexOf(entry);
1803
+ if (idx >= 0) this.retryQueue.splice(idx, 1);
1804
+ this.stats.retried++;
1805
+ processed++;
1806
+ }
1807
+ }
1808
+ this.retryQueue = this.retryQueue.filter((r) => r.attempts < this.config.maxRetries);
1809
+ return processed;
1810
+ }
1811
+ /**
1812
+ * Get retry queue length.
1813
+ */
1814
+ getRetryQueueLength() {
1815
+ return this.retryQueue.length;
1816
+ }
1817
+ // ===========================================================================
1818
+ // QUERIES
1819
+ // ===========================================================================
1820
+ /**
1821
+ * Check if an event has been processed.
1822
+ */
1823
+ isProcessed(eventId) {
1824
+ return this.processedEvents.has(eventId);
1825
+ }
1826
+ /**
1827
+ * Get comprehensive stats.
1828
+ */
1829
+ getStats() {
1830
+ return {
1831
+ ...this.stats,
1832
+ retryQueueLength: this.retryQueue.length
1833
+ };
1834
+ }
1835
+ /**
1836
+ * Create a webhook signature for testing purposes.
1837
+ */
1838
+ createSignature(rawBody, provider) {
1839
+ const secret = this.config.secrets[provider];
1840
+ if (!secret) throw new Error(`No secret for provider: ${provider}`);
1841
+ return computeHmac(rawBody, secret);
1842
+ }
1843
+ // ===========================================================================
1844
+ // TELEMETRY
1845
+ // ===========================================================================
1846
+ emitTelemetry(type, data) {
1847
+ this.config.telemetry?.record({
1848
+ type,
1849
+ severity: type.includes("error") || type.includes("failed") || type.includes("invalid") ? "error" : "info",
1850
+ agentId: "payment-webhook-service",
1851
+ data
1852
+ });
1853
+ }
1854
+ };
1855
+
1856
+ // src/economy/SubscriptionManager.ts
1857
+ var SubscriptionManager = class {
1858
+ config;
1859
+ subscriptions = /* @__PURE__ */ new Map();
1860
+ plans = /* @__PURE__ */ new Map();
1861
+ renewalCallback;
1862
+ subCounter = 0;
1863
+ constructor(config) {
1864
+ this.config = {
1865
+ gracePeriodDays: config?.gracePeriodDays ?? 3,
1866
+ maxFailedRenewals: config?.maxFailedRenewals ?? 3,
1867
+ telemetry: config?.telemetry
1868
+ };
1869
+ }
1870
+ // ===========================================================================
1871
+ // PLAN MANAGEMENT
1872
+ // ===========================================================================
1873
+ /**
1874
+ * Register a subscription plan.
1875
+ */
1876
+ registerPlan(plan) {
1877
+ this.plans.set(plan.id, { ...plan });
1878
+ }
1879
+ /**
1880
+ * Get a plan by ID.
1881
+ */
1882
+ getPlan(planId) {
1883
+ return this.plans.get(planId);
1884
+ }
1885
+ /**
1886
+ * List all plans.
1887
+ */
1888
+ listPlans() {
1889
+ return [...this.plans.values()];
1890
+ }
1891
+ // ===========================================================================
1892
+ // LIFECYCLE: CREATE
1893
+ // ===========================================================================
1894
+ /**
1895
+ * Create a new subscription.
1896
+ */
1897
+ create(subscriberId, planId, metadata) {
1898
+ const plan = this.plans.get(planId);
1899
+ if (!plan) {
1900
+ throw new Error(`Plan "${planId}" not found`);
1901
+ }
1902
+ const id = `sub-${++this.subCounter}`;
1903
+ const now = /* @__PURE__ */ new Date();
1904
+ const hasTrial = plan.trialDays > 0;
1905
+ const trialEnd = hasTrial ? new Date(now.getTime() + plan.trialDays * 864e5).toISOString() : null;
1906
+ const periodEnd = this.computePeriodEnd(now, plan.interval);
1907
+ const subscription = {
1908
+ id,
1909
+ subscriberId,
1910
+ planId,
1911
+ state: hasTrial ? "trial" : "active",
1912
+ amount: plan.amount,
1913
+ interval: plan.interval,
1914
+ createdAt: now.toISOString(),
1915
+ currentPeriodStart: now.toISOString(),
1916
+ currentPeriodEnd: periodEnd.toISOString(),
1917
+ trialEnd,
1918
+ inTrial: hasTrial,
1919
+ failedRenewals: 0,
1920
+ gracePeriodEnd: null,
1921
+ cancelledAt: null,
1922
+ cancelAtPeriodEnd: false,
1923
+ metadata
1924
+ };
1925
+ this.subscriptions.set(id, subscription);
1926
+ this.emitTelemetry("subscription_created", {
1927
+ subscriptionId: id,
1928
+ subscriberId,
1929
+ planId,
1930
+ state: subscription.state
1931
+ });
1932
+ return subscription;
1933
+ }
1934
+ // ===========================================================================
1935
+ // LIFECYCLE: RENEW
1936
+ // ===========================================================================
1937
+ /**
1938
+ * Attempt to renew a subscription.
1939
+ */
1940
+ async renew(subscriptionId) {
1941
+ const sub = this.requireSubscription(subscriptionId);
1942
+ if (sub.state === "cancelled" || sub.state === "expired") {
1943
+ return {
1944
+ success: false,
1945
+ subscription: sub,
1946
+ error: `Cannot renew ${sub.state} subscription`
1947
+ };
1948
+ }
1949
+ if (sub.inTrial && sub.trialEnd) {
1950
+ const trialEndMs = new Date(sub.trialEnd).getTime();
1951
+ if (Date.now() >= trialEndMs) {
1952
+ sub.inTrial = false;
1953
+ sub.state = "active";
1954
+ }
1955
+ }
1956
+ let paymentSuccess = true;
1957
+ if (this.renewalCallback) {
1958
+ try {
1959
+ paymentSuccess = await this.renewalCallback(subscriptionId, sub.amount);
1960
+ } catch {
1961
+ paymentSuccess = false;
1962
+ }
1963
+ }
1964
+ if (paymentSuccess) {
1965
+ const now = /* @__PURE__ */ new Date();
1966
+ sub.currentPeriodStart = now.toISOString();
1967
+ sub.currentPeriodEnd = this.computePeriodEnd(now, sub.interval).toISOString();
1968
+ sub.state = "active";
1969
+ sub.failedRenewals = 0;
1970
+ sub.gracePeriodEnd = null;
1971
+ if (sub.cancelAtPeriodEnd) {
1972
+ sub.state = "cancelled";
1973
+ sub.cancelledAt = now.toISOString();
1974
+ sub.cancelAtPeriodEnd = false;
1975
+ this.emitTelemetry("subscription_cancelled_at_period_end", { subscriptionId });
1976
+ return { success: true, subscription: sub };
1977
+ }
1978
+ this.emitTelemetry("subscription_renewed", { subscriptionId });
1979
+ return { success: true, subscription: sub };
1980
+ }
1981
+ sub.failedRenewals++;
1982
+ if (sub.failedRenewals >= this.config.maxFailedRenewals) {
1983
+ sub.state = "suspended";
1984
+ this.emitTelemetry("subscription_suspended", {
1985
+ subscriptionId,
1986
+ failedRenewals: sub.failedRenewals
1987
+ });
1988
+ return {
1989
+ success: false,
1990
+ subscription: sub,
1991
+ error: `Subscription suspended after ${sub.failedRenewals} failed renewals`
1992
+ };
1993
+ }
1994
+ sub.state = "past_due";
1995
+ const gracePeriodEnd = new Date(
1996
+ Date.now() + this.config.gracePeriodDays * 864e5
1997
+ ).toISOString();
1998
+ sub.gracePeriodEnd = gracePeriodEnd;
1999
+ this.emitTelemetry("subscription_renewal_failed", {
2000
+ subscriptionId,
2001
+ failedRenewals: sub.failedRenewals,
2002
+ gracePeriodEnd
2003
+ });
2004
+ return {
2005
+ success: false,
2006
+ subscription: sub,
2007
+ error: `Renewal failed (attempt ${sub.failedRenewals}/${this.config.maxFailedRenewals})`,
2008
+ enteredGracePeriod: true
2009
+ };
2010
+ }
2011
+ // ===========================================================================
2012
+ // LIFECYCLE: CANCEL
2013
+ // ===========================================================================
2014
+ /**
2015
+ * Cancel a subscription.
2016
+ * @param immediate If true, cancel immediately; otherwise cancel at period end.
2017
+ */
2018
+ cancel(subscriptionId, immediate = false) {
2019
+ const sub = this.requireSubscription(subscriptionId);
2020
+ if (sub.state === "cancelled" || sub.state === "expired") {
2021
+ throw new Error(`Subscription already ${sub.state}`);
2022
+ }
2023
+ if (immediate) {
2024
+ sub.state = "cancelled";
2025
+ sub.cancelledAt = (/* @__PURE__ */ new Date()).toISOString();
2026
+ sub.cancelAtPeriodEnd = false;
2027
+ } else {
2028
+ sub.cancelAtPeriodEnd = true;
2029
+ }
2030
+ this.emitTelemetry("subscription_cancel_requested", {
2031
+ subscriptionId,
2032
+ immediate
2033
+ });
2034
+ return sub;
2035
+ }
2036
+ // ===========================================================================
2037
+ // LIFECYCLE: SUSPEND / REACTIVATE
2038
+ // ===========================================================================
2039
+ /**
2040
+ * Suspend a subscription (e.g., after payment failures).
2041
+ */
2042
+ suspend(subscriptionId) {
2043
+ const sub = this.requireSubscription(subscriptionId);
2044
+ sub.state = "suspended";
2045
+ this.emitTelemetry("subscription_suspended", { subscriptionId });
2046
+ return sub;
2047
+ }
2048
+ /**
2049
+ * Reactivate a suspended or cancelled subscription.
2050
+ */
2051
+ reactivate(subscriptionId) {
2052
+ const sub = this.requireSubscription(subscriptionId);
2053
+ if (sub.state !== "suspended" && sub.state !== "cancelled" && sub.state !== "past_due") {
2054
+ throw new Error(`Cannot reactivate subscription in state: ${sub.state}`);
2055
+ }
2056
+ const now = /* @__PURE__ */ new Date();
2057
+ sub.state = "active";
2058
+ sub.failedRenewals = 0;
2059
+ sub.gracePeriodEnd = null;
2060
+ sub.cancelledAt = null;
2061
+ sub.cancelAtPeriodEnd = false;
2062
+ sub.currentPeriodStart = now.toISOString();
2063
+ sub.currentPeriodEnd = this.computePeriodEnd(now, sub.interval).toISOString();
2064
+ this.emitTelemetry("subscription_reactivated", { subscriptionId });
2065
+ return sub;
2066
+ }
2067
+ // ===========================================================================
2068
+ // RENEWAL CALLBACK
2069
+ // ===========================================================================
2070
+ /**
2071
+ * Set callback for renewal payment processing.
2072
+ * Should return true if payment succeeded.
2073
+ */
2074
+ onRenewal(callback) {
2075
+ this.renewalCallback = callback;
2076
+ }
2077
+ // ===========================================================================
2078
+ // QUERIES
2079
+ // ===========================================================================
2080
+ /**
2081
+ * Get a subscription by ID.
2082
+ */
2083
+ getSubscription(id) {
2084
+ return this.subscriptions.get(id);
2085
+ }
2086
+ /**
2087
+ * Get all subscriptions for a subscriber.
2088
+ */
2089
+ getSubscriberSubscriptions(subscriberId) {
2090
+ return [...this.subscriptions.values()].filter((s) => s.subscriberId === subscriberId);
2091
+ }
2092
+ /**
2093
+ * Get subscriptions by state.
2094
+ */
2095
+ getByState(state) {
2096
+ return [...this.subscriptions.values()].filter((s) => s.state === state);
2097
+ }
2098
+ /**
2099
+ * Get subscriptions due for renewal.
2100
+ */
2101
+ getDueForRenewal() {
2102
+ const now = Date.now();
2103
+ return [...this.subscriptions.values()].filter((s) => {
2104
+ if (s.state === "cancelled" || s.state === "expired" || s.state === "suspended") {
2105
+ return false;
2106
+ }
2107
+ return new Date(s.currentPeriodEnd).getTime() <= now;
2108
+ });
2109
+ }
2110
+ /**
2111
+ * Get subscriptions in grace period.
2112
+ */
2113
+ getInGracePeriod() {
2114
+ return [...this.subscriptions.values()].filter(
2115
+ (s) => s.state === "past_due" && s.gracePeriodEnd !== null
2116
+ );
2117
+ }
2118
+ /**
2119
+ * Check if grace period has expired for past_due subscriptions.
2120
+ */
2121
+ processExpiredGracePeriods() {
2122
+ const now = Date.now();
2123
+ const expired = [];
2124
+ for (const sub of this.subscriptions.values()) {
2125
+ if (sub.state === "past_due" && sub.gracePeriodEnd) {
2126
+ if (new Date(sub.gracePeriodEnd).getTime() <= now) {
2127
+ sub.state = "suspended";
2128
+ sub.gracePeriodEnd = null;
2129
+ expired.push(sub);
2130
+ this.emitTelemetry("subscription_grace_expired", { subscriptionId: sub.id });
2131
+ }
2132
+ }
2133
+ }
2134
+ return expired;
2135
+ }
2136
+ /**
2137
+ * Get stats.
2138
+ */
2139
+ getStats() {
2140
+ const byState = {};
2141
+ let totalMRR = 0;
2142
+ for (const sub of this.subscriptions.values()) {
2143
+ byState[sub.state] = (byState[sub.state] || 0) + 1;
2144
+ if (sub.state === "active" || sub.state === "trial") {
2145
+ totalMRR += this.normalizeToMonthly(sub.amount, sub.interval);
2146
+ }
2147
+ }
2148
+ return {
2149
+ total: this.subscriptions.size,
2150
+ byState,
2151
+ totalMRR,
2152
+ planCount: this.plans.size
2153
+ };
2154
+ }
2155
+ /**
2156
+ * Total subscription count.
2157
+ */
2158
+ getSubscriptionCount() {
2159
+ return this.subscriptions.size;
2160
+ }
2161
+ // ===========================================================================
2162
+ // INTERNALS
2163
+ // ===========================================================================
2164
+ requireSubscription(id) {
2165
+ const sub = this.subscriptions.get(id);
2166
+ if (!sub) throw new Error(`Subscription "${id}" not found`);
2167
+ return sub;
2168
+ }
2169
+ computePeriodEnd(start, interval) {
2170
+ const end = new Date(start);
2171
+ switch (interval) {
2172
+ case "daily":
2173
+ end.setDate(end.getDate() + 1);
2174
+ break;
2175
+ case "weekly":
2176
+ end.setDate(end.getDate() + 7);
2177
+ break;
2178
+ case "monthly":
2179
+ end.setMonth(end.getMonth() + 1);
2180
+ break;
2181
+ case "yearly":
2182
+ end.setFullYear(end.getFullYear() + 1);
2183
+ break;
2184
+ }
2185
+ return end;
2186
+ }
2187
+ normalizeToMonthly(amount, interval) {
2188
+ switch (interval) {
2189
+ case "daily":
2190
+ return amount * 30;
2191
+ case "weekly":
2192
+ return amount * 4;
2193
+ case "monthly":
2194
+ return amount;
2195
+ case "yearly":
2196
+ return Math.floor(amount / 12);
2197
+ }
2198
+ }
2199
+ emitTelemetry(type, data) {
2200
+ this.config.telemetry?.record({
2201
+ type,
2202
+ severity: type.includes("failed") || type.includes("suspended") ? "warning" : "info",
2203
+ agentId: "subscription-manager",
2204
+ data
2205
+ });
2206
+ }
2207
+ };
2208
+
2209
+ // src/economy/AgentBudgetEnforcer.ts
2210
+ var AgentBudgetEnforcer = class {
2211
+ trackers = /* @__PURE__ */ new Map();
2212
+ config;
2213
+ sessionCounter = 0;
2214
+ constructor(config) {
2215
+ this.config = {
2216
+ defaultBudget: {
2217
+ agentId: "",
2218
+ maxSpend: 1e7,
2219
+ // $10.00 default
2220
+ period: "daily",
2221
+ mode: "soft",
2222
+ warnThreshold: 0.8,
2223
+ circuitBreakerThreshold: 5,
2224
+ ...config?.defaultBudget
2225
+ },
2226
+ circuitBreakerResetMs: config?.circuitBreakerResetMs ?? 6e4,
2227
+ telemetry: config?.telemetry
2228
+ };
2229
+ }
2230
+ // ===========================================================================
2231
+ // BUDGET MANAGEMENT
2232
+ // ===========================================================================
2233
+ /**
2234
+ * Set budget for an agent.
2235
+ */
2236
+ setBudget(budget) {
2237
+ const existing = this.trackers.get(budget.agentId);
2238
+ if (existing) {
2239
+ existing.budget = { ...budget };
2240
+ } else {
2241
+ this.trackers.set(budget.agentId, {
2242
+ budget: { ...budget },
2243
+ spent: 0,
2244
+ requestCount: 0,
2245
+ periodStart: Date.now(),
2246
+ consecutiveFailures: 0,
2247
+ circuitBreakerTrippedAt: null,
2248
+ sessionId: `session-${++this.sessionCounter}`
2249
+ });
2250
+ }
2251
+ }
2252
+ /**
2253
+ * Get budget configuration for an agent.
2254
+ */
2255
+ getBudget(agentId) {
2256
+ return this.trackers.get(agentId)?.budget;
2257
+ }
2258
+ /**
2259
+ * Remove budget for an agent.
2260
+ */
2261
+ removeBudget(agentId) {
2262
+ return this.trackers.delete(agentId);
2263
+ }
2264
+ // ===========================================================================
2265
+ // AUTHORIZATION
2266
+ // ===========================================================================
2267
+ /**
2268
+ * Check if an agent is authorized to spend a given amount.
2269
+ */
2270
+ authorize(agentId, amount) {
2271
+ const tracker = this.getOrCreateTracker(agentId);
2272
+ this.checkPeriodReset(tracker);
2273
+ const state = this.buildState(tracker);
2274
+ if (state.circuitBreaker.isOpen) {
2275
+ if (tracker.circuitBreakerTrippedAt) {
2276
+ const elapsed = Date.now() - tracker.circuitBreakerTrippedAt;
2277
+ if (elapsed >= this.config.circuitBreakerResetMs) {
2278
+ tracker.consecutiveFailures = 0;
2279
+ tracker.circuitBreakerTrippedAt = null;
2280
+ } else {
2281
+ this.emitTelemetry("budget_circuit_breaker_blocked", { agentId, amount });
2282
+ return {
2283
+ authorized: false,
2284
+ reason: `Circuit breaker open: ${tracker.consecutiveFailures} consecutive failures. Resets in ${Math.ceil((this.config.circuitBreakerResetMs - elapsed) / 1e3)}s`,
2285
+ state: this.buildState(tracker)
2286
+ };
2287
+ }
2288
+ }
2289
+ }
2290
+ const wouldExceed = tracker.spent + amount > tracker.budget.maxSpend;
2291
+ const warnThreshold = tracker.budget.warnThreshold ?? 0.8;
2292
+ const atWarningLevel = (tracker.spent + amount) / tracker.budget.maxSpend >= warnThreshold;
2293
+ if (wouldExceed) {
2294
+ switch (tracker.budget.mode) {
2295
+ case "hard":
2296
+ this.emitTelemetry("budget_hard_denied", {
2297
+ agentId,
2298
+ amount,
2299
+ spent: tracker.spent,
2300
+ limit: tracker.budget.maxSpend
2301
+ });
2302
+ return {
2303
+ authorized: false,
2304
+ reason: `Budget exhausted (hard limit): spent ${tracker.spent} + ${amount} > limit ${tracker.budget.maxSpend}`,
2305
+ state: this.buildState(tracker)
2306
+ };
2307
+ case "soft":
2308
+ this.emitTelemetry("budget_soft_denied", { agentId, amount, spent: tracker.spent });
2309
+ return {
2310
+ authorized: false,
2311
+ reason: `Budget exhausted (soft limit): spent ${tracker.spent} + ${amount} > limit ${tracker.budget.maxSpend}`,
2312
+ state: this.buildState(tracker)
2313
+ };
2314
+ case "warn":
2315
+ this.emitTelemetry("budget_warn_overspend", { agentId, amount });
2316
+ return {
2317
+ authorized: true,
2318
+ state: this.buildState(tracker),
2319
+ warning: true,
2320
+ warningMessage: `Budget exceeded: spent ${tracker.spent} + ${amount} > limit ${tracker.budget.maxSpend}`
2321
+ };
2322
+ }
2323
+ }
2324
+ if (atWarningLevel && !wouldExceed) {
2325
+ return {
2326
+ authorized: true,
2327
+ state: this.buildState(tracker),
2328
+ warning: true,
2329
+ warningMessage: `Approaching budget limit: ${Math.round((tracker.spent + amount) / tracker.budget.maxSpend * 100)}% used`
2330
+ };
2331
+ }
2332
+ return {
2333
+ authorized: true,
2334
+ state: this.buildState(tracker)
2335
+ };
2336
+ }
2337
+ /**
2338
+ * Record a spend. Call after a successful tool execution.
2339
+ */
2340
+ recordSpend(agentId, amount) {
2341
+ const tracker = this.getOrCreateTracker(agentId);
2342
+ this.checkPeriodReset(tracker);
2343
+ tracker.spent += amount;
2344
+ tracker.requestCount++;
2345
+ tracker.consecutiveFailures = 0;
2346
+ this.emitTelemetry("budget_spend_recorded", { agentId, amount, totalSpent: tracker.spent });
2347
+ }
2348
+ /**
2349
+ * Record a failure. Increments circuit breaker counter.
2350
+ */
2351
+ recordFailure(agentId) {
2352
+ const tracker = this.getOrCreateTracker(agentId);
2353
+ tracker.consecutiveFailures++;
2354
+ const threshold = tracker.budget.circuitBreakerThreshold ?? 5;
2355
+ if (tracker.consecutiveFailures >= threshold && !tracker.circuitBreakerTrippedAt) {
2356
+ tracker.circuitBreakerTrippedAt = Date.now();
2357
+ this.emitTelemetry("budget_circuit_breaker_tripped", {
2358
+ agentId,
2359
+ failures: tracker.consecutiveFailures,
2360
+ threshold
2361
+ });
2362
+ }
2363
+ }
2364
+ // ===========================================================================
2365
+ // QUERIES
2366
+ // ===========================================================================
2367
+ /**
2368
+ * Get current budget state for an agent.
2369
+ */
2370
+ getState(agentId) {
2371
+ const tracker = this.trackers.get(agentId);
2372
+ if (!tracker) return void 0;
2373
+ this.checkPeriodReset(tracker);
2374
+ return this.buildState(tracker);
2375
+ }
2376
+ /**
2377
+ * Get all agent budget states.
2378
+ */
2379
+ getAllStates() {
2380
+ return [...this.trackers.keys()].map((id) => this.getState(id)).filter(Boolean);
2381
+ }
2382
+ /**
2383
+ * Get agents that are over budget.
2384
+ */
2385
+ getOverBudgetAgents() {
2386
+ return this.getAllStates().filter((s) => s.exhausted || s.circuitBreaker.isOpen);
2387
+ }
2388
+ /**
2389
+ * Reset an agent's period spending.
2390
+ */
2391
+ resetSpending(agentId) {
2392
+ const tracker = this.trackers.get(agentId);
2393
+ if (tracker) {
2394
+ tracker.spent = 0;
2395
+ tracker.requestCount = 0;
2396
+ tracker.periodStart = Date.now();
2397
+ tracker.consecutiveFailures = 0;
2398
+ tracker.circuitBreakerTrippedAt = null;
2399
+ tracker.sessionId = `session-${++this.sessionCounter}`;
2400
+ }
2401
+ }
2402
+ /**
2403
+ * Reset circuit breaker for an agent.
2404
+ */
2405
+ resetCircuitBreaker(agentId) {
2406
+ const tracker = this.trackers.get(agentId);
2407
+ if (tracker) {
2408
+ tracker.consecutiveFailures = 0;
2409
+ tracker.circuitBreakerTrippedAt = null;
2410
+ }
2411
+ }
2412
+ // ===========================================================================
2413
+ // INTERNALS
2414
+ // ===========================================================================
2415
+ getOrCreateTracker(agentId) {
2416
+ let tracker = this.trackers.get(agentId);
2417
+ if (!tracker) {
2418
+ tracker = {
2419
+ budget: { ...this.config.defaultBudget, agentId },
2420
+ spent: 0,
2421
+ requestCount: 0,
2422
+ periodStart: Date.now(),
2423
+ consecutiveFailures: 0,
2424
+ circuitBreakerTrippedAt: null,
2425
+ sessionId: `session-${++this.sessionCounter}`
2426
+ };
2427
+ this.trackers.set(agentId, tracker);
2428
+ }
2429
+ return tracker;
2430
+ }
2431
+ checkPeriodReset(tracker) {
2432
+ const now = Date.now();
2433
+ const elapsed = now - tracker.periodStart;
2434
+ let shouldReset = false;
2435
+ switch (tracker.budget.period) {
2436
+ case "per-request":
2437
+ shouldReset = tracker.requestCount > 0;
2438
+ break;
2439
+ case "per-session":
2440
+ break;
2441
+ case "daily":
2442
+ shouldReset = elapsed >= 864e5;
2443
+ break;
2444
+ case "monthly":
2445
+ shouldReset = elapsed >= 30 * 864e5;
2446
+ break;
2447
+ }
2448
+ if (shouldReset) {
2449
+ tracker.spent = 0;
2450
+ tracker.requestCount = 0;
2451
+ tracker.periodStart = now;
2452
+ tracker.sessionId = `session-${++this.sessionCounter}`;
2453
+ }
2454
+ }
2455
+ buildState(tracker) {
2456
+ const remaining = Math.max(0, tracker.budget.maxSpend - tracker.spent);
2457
+ const warnThreshold = tracker.budget.warnThreshold ?? 0.8;
2458
+ const cbThreshold = tracker.budget.circuitBreakerThreshold ?? 5;
2459
+ return {
2460
+ agentId: tracker.budget.agentId,
2461
+ spent: tracker.spent,
2462
+ limit: tracker.budget.maxSpend,
2463
+ remaining,
2464
+ exhausted: remaining === 0,
2465
+ warning: tracker.spent / tracker.budget.maxSpend >= warnThreshold,
2466
+ mode: tracker.budget.mode,
2467
+ period: tracker.budget.period,
2468
+ periodStart: new Date(tracker.periodStart).toISOString(),
2469
+ requestCount: tracker.requestCount,
2470
+ circuitBreaker: {
2471
+ isOpen: tracker.circuitBreakerTrippedAt !== null && Date.now() - tracker.circuitBreakerTrippedAt < this.config.circuitBreakerResetMs,
2472
+ consecutiveFailures: tracker.consecutiveFailures,
2473
+ threshold: cbThreshold,
2474
+ trippedAt: tracker.circuitBreakerTrippedAt ? new Date(tracker.circuitBreakerTrippedAt).toISOString() : null,
2475
+ resetAt: tracker.circuitBreakerTrippedAt ? new Date(
2476
+ tracker.circuitBreakerTrippedAt + this.config.circuitBreakerResetMs
2477
+ ).toISOString() : null
2478
+ }
2479
+ };
2480
+ }
2481
+ emitTelemetry(type, data) {
2482
+ this.config.telemetry?.record({
2483
+ type,
2484
+ severity: type.includes("denied") || type.includes("tripped") ? "warning" : "info",
2485
+ agentId: data?.agentId || "budget-enforcer",
2486
+ data
2487
+ });
2488
+ }
2489
+ };
2490
+
2491
+ // src/economy/_core-stubs.ts
2492
+ var PLATFORM_BUDGETS = {
2493
+ quest3: {
2494
+ particles: 5e3,
2495
+ physicsBodies: 200,
2496
+ audioSources: 16,
2497
+ meshInstances: 500,
2498
+ gaussians: 18e4,
2499
+ shaderPasses: 4,
2500
+ networkMsgs: 60,
2501
+ agentCount: 10,
2502
+ memoryMB: 512,
2503
+ gpuDrawCalls: 200
2504
+ },
2505
+ "desktop-vr": {
2506
+ particles: 5e4,
2507
+ physicsBodies: 2e3,
2508
+ audioSources: 64,
2509
+ meshInstances: 5e3,
2510
+ gaussians: 2e6,
2511
+ shaderPasses: 16,
2512
+ networkMsgs: 200,
2513
+ agentCount: 50,
2514
+ memoryMB: 4096,
2515
+ gpuDrawCalls: 2e3
2516
+ },
2517
+ webgpu: {
2518
+ particles: 2e4,
2519
+ physicsBodies: 500,
2520
+ audioSources: 32,
2521
+ meshInstances: 2e3,
2522
+ gaussians: 5e5,
2523
+ shaderPasses: 8,
2524
+ networkMsgs: 100,
2525
+ agentCount: 20,
2526
+ memoryMB: 1024,
2527
+ gpuDrawCalls: 500
2528
+ },
2529
+ "mobile-ar": {
2530
+ particles: 2e3,
2531
+ physicsBodies: 50,
2532
+ audioSources: 8,
2533
+ meshInstances: 200,
2534
+ gaussians: 1e5,
2535
+ shaderPasses: 2,
2536
+ networkMsgs: 30,
2537
+ agentCount: 5,
2538
+ memoryMB: 256,
2539
+ gpuDrawCalls: 100
2540
+ }
2541
+ };
2542
+ var TRAIT_RESOURCE_COSTS = {
2543
+ "@mesh": { meshInstances: 1, gpuDrawCalls: 1 },
2544
+ "@material": { shaderPasses: 1, gpuDrawCalls: 1, memoryMB: 0.5 },
2545
+ "@shader": { shaderPasses: 1, gpuDrawCalls: 1 },
2546
+ "@advanced_pbr": { shaderPasses: 2, gpuDrawCalls: 1, memoryMB: 1 },
2547
+ "@advanced_lighting": { gpuDrawCalls: 2, shaderPasses: 1 },
2548
+ "@advanced_texturing": { memoryMB: 2, gpuDrawCalls: 1 },
2549
+ "@light": { gpuDrawCalls: 1, shaderPasses: 1 },
2550
+ "@lighting": { gpuDrawCalls: 2, shaderPasses: 1 },
2551
+ "@global_illumination": { gpuDrawCalls: 4, shaderPasses: 3, memoryMB: 8 },
2552
+ "@ray_tracing": { gpuDrawCalls: 8, shaderPasses: 4, memoryMB: 16 },
2553
+ "@screen_space_effects": { shaderPasses: 2, gpuDrawCalls: 2 },
2554
+ "@subsurface_scattering": { shaderPasses: 2, gpuDrawCalls: 1 },
2555
+ "@rendering": { meshInstances: 1, gpuDrawCalls: 1, memoryMB: 1 },
2556
+ "@render_network": { gpuDrawCalls: 2, networkMsgs: 5, memoryMB: 4 },
2557
+ "@particle": { particles: 100, gpuDrawCalls: 1, memoryMB: 2 },
2558
+ "@vfx": { particles: 200, shaderPasses: 1, gpuDrawCalls: 2 },
2559
+ "@volumetric": { gpuDrawCalls: 3, shaderPasses: 2, memoryMB: 4 },
2560
+ "@volumetric_window": { gpuDrawCalls: 2, memoryMB: 2 },
2561
+ "@gaussian": { gaussians: 1e5, memoryMB: 10 },
2562
+ "@gaussian_splat": { gaussians: 1e5, memoryMB: 10 },
2563
+ "@multiview_gaussian_renderer": { gaussians: 2e5, memoryMB: 20, gpuDrawCalls: 4 },
2564
+ "@nerf": { gpuDrawCalls: 4, memoryMB: 16, shaderPasses: 2 },
2565
+ "@physics": { physicsBodies: 1 },
2566
+ "@rigidbody": { physicsBodies: 1 },
2567
+ "@collider": { physicsBodies: 1 },
2568
+ "@joint": { physicsBodies: 2 },
2569
+ "@trigger": { physicsBodies: 1 },
2570
+ "@fluid_simulation": { particles: 500, physicsBodies: 10, memoryMB: 8 },
2571
+ "@advanced_cloth": { particles: 200, physicsBodies: 5, memoryMB: 4 },
2572
+ "@granular_material": { particles: 300, physicsBodies: 8, memoryMB: 6 },
2573
+ "@voronoi_fracture": { physicsBodies: 20, meshInstances: 20, memoryMB: 4 },
2574
+ "@audio": { audioSources: 1 },
2575
+ "@spatial_audio": { audioSources: 1 },
2576
+ "@environmental_audio": { audioSources: 4, memoryMB: 2 },
2577
+ "@voice_mesh": { audioSources: 1, networkMsgs: 10 },
2578
+ "@voice_input": { audioSources: 1, memoryMB: 1 },
2579
+ "@voice_output": { audioSources: 1, memoryMB: 1 },
2580
+ "@lip_sync": { memoryMB: 2, gpuDrawCalls: 1 },
2581
+ "@ambisonics": { audioSources: 4, memoryMB: 4 },
2582
+ "@networked": { networkMsgs: 1 },
2583
+ "@networked_avatar": { networkMsgs: 10, meshInstances: 1, memoryMB: 2 },
2584
+ "@lobby": { networkMsgs: 5, memoryMB: 2 },
2585
+ "@mqtt_sink": { networkMsgs: 5 },
2586
+ "@mqtt_source": { networkMsgs: 5 },
2587
+ "@sync_tier": { networkMsgs: 2 },
2588
+ "@crdt_room": { networkMsgs: 10, memoryMB: 4 },
2589
+ "@shareplay": { networkMsgs: 5, memoryMB: 2 },
2590
+ "@agent": { agentCount: 1, memoryMB: 5 },
2591
+ "@npc": { agentCount: 1, memoryMB: 3, physicsBodies: 1 },
2592
+ "@npc_ai": { agentCount: 1, memoryMB: 8 },
2593
+ "@ai_npc_brain": { agentCount: 1, memoryMB: 10 },
2594
+ "@multi_agent": { agentCount: 3, memoryMB: 15 },
2595
+ "@agent_discovery": { agentCount: 1, memoryMB: 2 },
2596
+ "@neural_animation": { memoryMB: 8, gpuDrawCalls: 2 },
2597
+ "@neural_forge": { memoryMB: 16, agentCount: 1 },
2598
+ "@local_llm": { memoryMB: 32, agentCount: 1 },
2599
+ "@rag_knowledge": { memoryMB: 8 },
2600
+ "@embedding_search": { memoryMB: 4 },
2601
+ "@vector_db": { memoryMB: 8 },
2602
+ "@stable_diffusion": { memoryMB: 32, gpuDrawCalls: 1 },
2603
+ "@diffusion_realtime": { memoryMB: 16, gpuDrawCalls: 2 },
2604
+ "@vision": { memoryMB: 4 },
2605
+ "@pose_estimation": { memoryMB: 4, agentCount: 1 },
2606
+ "@object_tracking": { memoryMB: 4 },
2607
+ "@hand_mesh_ai": { memoryMB: 4, gpuDrawCalls: 1 },
2608
+ "@animation": { gpuDrawCalls: 1, memoryMB: 2 },
2609
+ "@skeleton": { gpuDrawCalls: 1, memoryMB: 1 },
2610
+ "@ik": { memoryMB: 1 },
2611
+ "@morph": { gpuDrawCalls: 1, memoryMB: 1 },
2612
+ "@character": { physicsBodies: 1, meshInstances: 1, gpuDrawCalls: 2, memoryMB: 4 },
2613
+ "@emotion_directive": { memoryMB: 1 },
2614
+ "@dialog": { memoryMB: 2 },
2615
+ "@scene_reconstruction": { meshInstances: 10, memoryMB: 8, gpuDrawCalls: 5 },
2616
+ "@realitykit_mesh": { meshInstances: 5, memoryMB: 4, gpuDrawCalls: 3 },
2617
+ "@openxr_hal": { memoryMB: 2 },
2618
+ "@spatial_navigation": { memoryMB: 2 },
2619
+ "@spatial_persona": { meshInstances: 1, memoryMB: 2, networkMsgs: 5 },
2620
+ "@spatial_awareness": { memoryMB: 2 },
2621
+ "@orbital": { physicsBodies: 1, memoryMB: 1 },
2622
+ "@grabbable": { physicsBodies: 1 },
2623
+ "@pressable": { physicsBodies: 1 },
2624
+ "@slidable": { physicsBodies: 1 },
2625
+ "@wot_thing": { networkMsgs: 2, memoryMB: 1 },
2626
+ "@urdf_robot": { physicsBodies: 10, meshInstances: 10, memoryMB: 4 },
2627
+ "@computer_use": { memoryMB: 4 },
2628
+ "@pid_controller": { memoryMB: 0.5 },
2629
+ "@biofeedback": { memoryMB: 1 }
2630
+ };
2631
+
2632
+ // src/economy/UnifiedBudgetOptimizer.ts
2633
+ var DEFAULT_TRAIT_UTILITIES = {
2634
+ // ── Core (required, high utility) ──
2635
+ "@mesh": { baseUtility: 100, category: "visual", required: true, minLODLevel: 0 },
2636
+ "@material": { baseUtility: 95, category: "visual", required: true, minLODLevel: 0 },
2637
+ "@physics": { baseUtility: 90, category: "physics", required: true, minLODLevel: 0 },
2638
+ "@rigidbody": { baseUtility: 90, category: "physics", required: true, minLODLevel: 0 },
2639
+ "@collider": { baseUtility: 88, category: "physics", required: true, minLODLevel: 0 },
2640
+ "@rendering": { baseUtility: 95, category: "visual", required: true, minLODLevel: 0 },
2641
+ "@character": { baseUtility: 92, category: "visual", required: true, minLODLevel: 0 },
2642
+ "@networked": { baseUtility: 85, category: "network", required: true, minLODLevel: 0 },
2643
+ // C6 Layer 2 re-score: @agent was hand-assigned at 85 (required). Data-derived
2644
+ // analysis shows agents are a luxury in most rendering scenes — only ~35% of
2645
+ // compositions use @agent, and it consumes 5MB memory. Downgraded to non-required
2646
+ // enhancement. Scenes that genuinely need agents can override via custom utilities.
2647
+ "@agent": { baseUtility: 35, category: "ai", required: false, minLODLevel: 2 },
2648
+ // ── Important quality (droppable at high LOD) ──
2649
+ "@light": { baseUtility: 80, category: "visual", required: false, minLODLevel: 3 },
2650
+ "@lighting": { baseUtility: 78, category: "visual", required: false, minLODLevel: 3 },
2651
+ "@shader": { baseUtility: 75, category: "visual", required: false, minLODLevel: 2 },
2652
+ "@advanced_pbr": { baseUtility: 72, category: "visual", required: false, minLODLevel: 2 },
2653
+ "@particle": { baseUtility: 70, category: "visual", required: false, minLODLevel: 2 },
2654
+ "@audio": { baseUtility: 75, category: "audio", required: false, minLODLevel: 3 },
2655
+ "@spatial_audio": { baseUtility: 72, category: "audio", required: false, minLODLevel: 2 },
2656
+ "@animation": { baseUtility: 78, category: "visual", required: false, minLODLevel: 3 },
2657
+ "@skeleton": { baseUtility: 76, category: "visual", required: false, minLODLevel: 3 },
2658
+ // ── Enhancement (drop at medium LOD) ──
2659
+ "@vfx": { baseUtility: 60, category: "visual", required: false, minLODLevel: 2 },
2660
+ "@volumetric": { baseUtility: 55, category: "visual", required: false, minLODLevel: 2 },
2661
+ "@advanced_lighting": { baseUtility: 58, category: "visual", required: false, minLODLevel: 2 },
2662
+ "@advanced_texturing": { baseUtility: 56, category: "visual", required: false, minLODLevel: 2 },
2663
+ "@screen_space_effects": { baseUtility: 52, category: "visual", required: false, minLODLevel: 2 },
2664
+ "@environmental_audio": { baseUtility: 55, category: "audio", required: false, minLODLevel: 2 },
2665
+ "@npc": { baseUtility: 65, category: "ai", required: false, minLODLevel: 3 },
2666
+ "@npc_ai": { baseUtility: 62, category: "ai", required: false, minLODLevel: 3 },
2667
+ "@fluid_simulation": { baseUtility: 50, category: "physics", required: false, minLODLevel: 1 },
2668
+ "@advanced_cloth": { baseUtility: 48, category: "physics", required: false, minLODLevel: 1 },
2669
+ "@joint": { baseUtility: 65, category: "physics", required: false, minLODLevel: 3 },
2670
+ // ── Nice-to-have (drop early) ──
2671
+ "@subsurface_scattering": {
2672
+ baseUtility: 38,
2673
+ category: "visual",
2674
+ required: false,
2675
+ minLODLevel: 1
2676
+ },
2677
+ "@ambisonics": { baseUtility: 35, category: "audio", required: false, minLODLevel: 1 },
2678
+ "@voronoi_fracture": { baseUtility: 40, category: "physics", required: false, minLODLevel: 1 },
2679
+ "@granular_material": { baseUtility: 38, category: "physics", required: false, minLODLevel: 1 },
2680
+ "@lip_sync": { baseUtility: 42, category: "visual", required: false, minLODLevel: 1 },
2681
+ // ── Luxury (drop first) ──
2682
+ "@ray_tracing": { baseUtility: 20, category: "visual", required: false, minLODLevel: 1 },
2683
+ "@global_illumination": { baseUtility: 22, category: "visual", required: false, minLODLevel: 1 },
2684
+ "@nerf": { baseUtility: 18, category: "visual", required: false, minLODLevel: 1 },
2685
+ "@multiview_gaussian_renderer": {
2686
+ baseUtility: 25,
2687
+ category: "visual",
2688
+ required: false,
2689
+ minLODLevel: 1
2690
+ },
2691
+ // C6 Layer 2 re-score: @gaussian/@gaussian_splat were hand-assigned at 65.
2692
+ // Data-derived analysis: 100K gaussians on Quest 3 = 55% of total GPU budget,
2693
+ // making the value/cost ratio much lower than hand-scores suggested.
2694
+ // Downgraded by 45 points to reflect actual GPU cost relative to utility.
2695
+ "@gaussian": { baseUtility: 20, category: "visual", required: false, minLODLevel: 1 },
2696
+ "@gaussian_splat": { baseUtility: 20, category: "visual", required: false, minLODLevel: 1 },
2697
+ "@stable_diffusion": { baseUtility: 15, category: "ai", required: false, minLODLevel: 1 },
2698
+ "@diffusion_realtime": { baseUtility: 18, category: "ai", required: false, minLODLevel: 1 },
2699
+ "@local_llm": { baseUtility: 30, category: "ai", required: false, minLODLevel: 1 }
2700
+ };
2701
+ var PLATFORM_LOD_SCALING = {
2702
+ quest3: [1, 0.6, 0.3, 0.12, 0.04],
2703
+ // Aggressive — tight budget
2704
+ "mobile-ar": [1, 0.5, 0.2, 0.08, 0.02],
2705
+ // Most aggressive — tightest budget
2706
+ webgpu: [1, 0.7, 0.4, 0.18, 0.06],
2707
+ // Moderate — matches default
2708
+ "desktop-vr": [1, 0.85, 0.6, 0.3, 0.1],
2709
+ // Gentle — plenty of headroom
2710
+ visionos: [1, 0.8, 0.5, 0.25, 0.08]
2711
+ // Moderate-gentle — good hardware
2712
+ };
2713
+ var DEFAULT_COST_FLOOR = {
2714
+ perGaussian: 0.01,
2715
+ // 10K gaussians = $0.10 minimum
2716
+ perDrawCall: 1e3,
2717
+ // $0.001 per draw call
2718
+ perMemoryMB: 5e3,
2719
+ // $0.005 per MB
2720
+ perParticle: 0.1,
2721
+ // 1000 particles = $0.10 minimum
2722
+ perPhysicsBody: 500,
2723
+ // $0.0005 per body
2724
+ baseFee: 1e5
2725
+ // $0.10 base fee on all marketplace traits
2726
+ };
2727
+ var DEFAULT_LOD_SCALING = [1, 0.7, 0.4, 0.18, 0.06];
2728
+ var UnifiedBudgetOptimizer = class {
2729
+ platform;
2730
+ costFloor;
2731
+ traitUtilities;
2732
+ lodScaling;
2733
+ economicBudget;
2734
+ economicSpent;
2735
+ constructor(config) {
2736
+ this.platform = config.platform;
2737
+ this.costFloor = config.costFloor;
2738
+ this.lodScaling = config.lodScaling ?? PLATFORM_LOD_SCALING[config.platform] ?? DEFAULT_LOD_SCALING;
2739
+ this.economicBudget = config.economicBudget ?? 0;
2740
+ this.economicSpent = config.economicSpent ?? 0;
2741
+ this.traitUtilities = /* @__PURE__ */ new Map();
2742
+ for (const [trait, util] of Object.entries(DEFAULT_TRAIT_UTILITIES)) {
2743
+ this.traitUtilities.set(trait, { trait, ...util });
2744
+ }
2745
+ if (config.traitUtilities) {
2746
+ for (const [trait, util] of config.traitUtilities) {
2747
+ this.traitUtilities.set(trait, util);
2748
+ }
2749
+ }
2750
+ }
2751
+ // ===========================================================================
2752
+ // CORE: Equimarginal Allocation
2753
+ // ===========================================================================
2754
+ /**
2755
+ * Allocate traits across a resource budget using the equimarginal principle.
2756
+ * Instead of greedily dropping the deepest LOD first, this sorts by value/cost
2757
+ * ratio and drops traits with the lowest marginal utility first.
2758
+ *
2759
+ * @param nodes - Resource usage nodes (traits + counts)
2760
+ * @param maxLOD - Maximum LOD level to consider (default: 4)
2761
+ * @returns Allocation decisions for each trait
2762
+ */
2763
+ allocate(nodes, maxLOD = 4) {
2764
+ const limits = PLATFORM_BUDGETS[this.platform];
2765
+ if (!limits) {
2766
+ return this.flattenToAllocations(nodes, 0);
2767
+ }
2768
+ const allocations = this.flattenToAllocations(nodes, 0);
2769
+ let pressure = this.computeResourcePressure(allocations, limits);
2770
+ if (pressure.maxPressure <= 1) {
2771
+ return allocations;
2772
+ }
2773
+ for (let lod = 1; lod <= maxLOD && pressure.maxPressure > 1; lod++) {
2774
+ const candidates = allocations.filter((a) => a.included && !this.isRequired(a.trait) && this.canDropAtLOD(a.trait, lod)).sort((a, b) => a.valueCostRatio - b.valueCostRatio);
2775
+ for (const candidate of candidates) {
2776
+ if (pressure.maxPressure <= 1) break;
2777
+ const idx = allocations.findIndex((a) => a.trait === candidate.trait);
2778
+ if (idx >= 0) {
2779
+ allocations[idx] = this.buildAllocation(candidate.trait, lod, 1);
2780
+ }
2781
+ pressure = this.computeResourcePressure(allocations, limits);
2782
+ }
2783
+ }
2784
+ if (pressure.maxPressure > 1) {
2785
+ const excludeable = allocations.filter((a) => a.included && !this.isRequired(a.trait)).sort((a, b) => a.valueCostRatio - b.valueCostRatio);
2786
+ for (const candidate of excludeable) {
2787
+ if (pressure.maxPressure <= 1) break;
2788
+ const idx = allocations.findIndex((a) => a.trait === candidate.trait);
2789
+ if (idx >= 0) {
2790
+ allocations[idx] = {
2791
+ ...allocations[idx],
2792
+ included: false,
2793
+ excludeReason: `Excluded to fit ${this.platform} budget (value/cost ratio: ${candidate.valueCostRatio.toFixed(2)})`
2794
+ };
2795
+ }
2796
+ pressure = this.computeResourcePressure(allocations, limits);
2797
+ }
2798
+ }
2799
+ return allocations;
2800
+ }
2801
+ // ===========================================================================
2802
+ // RESOURCE COST FLOOR PRICING
2803
+ // ===========================================================================
2804
+ /**
2805
+ * Calculate the minimum marketplace price for a trait based on its resource cost.
2806
+ * Prevents economic denial-of-rendering attacks where a cheap marketplace trait
2807
+ * consumes massive GPU resources.
2808
+ *
2809
+ * @param traitName - The trait to price
2810
+ * @param instanceCount - How many instances (default: 1)
2811
+ * @returns Minimum price in USDC base units (6 decimals)
2812
+ */
2813
+ calculateCostFloor(traitName, instanceCount = 1) {
2814
+ const normalized = traitName.startsWith("@") ? traitName : `@${traitName}`;
2815
+ const costs = TRAIT_RESOURCE_COSTS[normalized];
2816
+ if (!costs) return this.costFloor.baseFee;
2817
+ let floor = this.costFloor.baseFee;
2818
+ if (costs.gaussians) floor += costs.gaussians * instanceCount * this.costFloor.perGaussian;
2819
+ if (costs.gpuDrawCalls)
2820
+ floor += costs.gpuDrawCalls * instanceCount * this.costFloor.perDrawCall;
2821
+ if (costs.memoryMB) floor += costs.memoryMB * instanceCount * this.costFloor.perMemoryMB;
2822
+ if (costs.particles) floor += costs.particles * instanceCount * this.costFloor.perParticle;
2823
+ if (costs.physicsBodies)
2824
+ floor += costs.physicsBodies * instanceCount * this.costFloor.perPhysicsBody;
2825
+ return Math.ceil(floor);
2826
+ }
2827
+ /**
2828
+ * Validate that a marketplace listing price meets the resource cost floor.
2829
+ *
2830
+ * @param traitName - The trait being listed
2831
+ * @param listPrice - Proposed listing price (USDC base units)
2832
+ * @param instanceCount - Expected instance count
2833
+ * @returns Validation result
2834
+ */
2835
+ validateMarketplacePrice(traitName, listPrice, instanceCount = 1) {
2836
+ const floor = this.calculateCostFloor(traitName, instanceCount);
2837
+ const deficit = Math.max(0, floor - listPrice);
2838
+ if (listPrice >= floor) {
2839
+ return {
2840
+ valid: true,
2841
+ floor,
2842
+ deficit: 0,
2843
+ message: `Price ${listPrice} meets resource cost floor of ${floor}`
2844
+ };
2845
+ }
2846
+ return {
2847
+ valid: false,
2848
+ floor,
2849
+ deficit,
2850
+ message: `Price ${listPrice} is below resource cost floor of ${floor}. The trait's GPU/memory cost exceeds its economic price by ${deficit} USDC base units.`
2851
+ };
2852
+ }
2853
+ // ===========================================================================
2854
+ // UNIFIED BUDGET STATE
2855
+ // ===========================================================================
2856
+ /**
2857
+ * Get a unified view of budget pressure across economy + rendering.
2858
+ *
2859
+ * @param agentId - Agent identifier
2860
+ * @param nodes - Current resource usage nodes
2861
+ * @param economicSpent - Current economic spend (USDC base units)
2862
+ * @param economicLimit - Economic budget limit (USDC base units)
2863
+ * @returns Unified budget state
2864
+ */
2865
+ getUnifiedState(agentId, nodes, economicSpent, economicLimit) {
2866
+ const spent = economicSpent ?? this.economicSpent;
2867
+ const limit = economicLimit ?? this.economicBudget;
2868
+ const economicPressure = limit > 0 ? spent / limit : 0;
2869
+ const allocations = this.flattenToAllocations(nodes, 0);
2870
+ const limits = PLATFORM_BUDGETS[this.platform] ?? {};
2871
+ const pressure = this.computeResourcePressure(allocations, limits);
2872
+ const overallPressure = Math.max(economicPressure, pressure.maxPressure);
2873
+ let suggestedLOD = 0;
2874
+ if (overallPressure > 0.95) suggestedLOD = 3;
2875
+ else if (overallPressure > 0.8) suggestedLOD = 2;
2876
+ else if (overallPressure > 0.6) suggestedLOD = 1;
2877
+ const shedCandidates = allocations.filter((a) => a.included && !this.isRequired(a.trait)).sort((a, b) => a.valueCostRatio - b.valueCostRatio).slice(0, 10);
2878
+ return {
2879
+ agentId,
2880
+ economicPressure: Math.min(1, economicPressure),
2881
+ resourcePressure: pressure.perCategory,
2882
+ overallPressure: Math.min(1, overallPressure),
2883
+ suggestedLOD,
2884
+ hardLimitBreached: pressure.maxPressure > 1 || economicPressure > 1,
2885
+ shedCandidates
2886
+ };
2887
+ }
2888
+ // ===========================================================================
2889
+ // UTILITY QUERIES
2890
+ // ===========================================================================
2891
+ /**
2892
+ * Get the utility score for a trait.
2893
+ */
2894
+ getUtility(traitName) {
2895
+ const normalized = traitName.startsWith("@") ? traitName : `@${traitName}`;
2896
+ return this.traitUtilities.get(normalized);
2897
+ }
2898
+ /**
2899
+ * Set custom utility for a trait.
2900
+ */
2901
+ setUtility(utility) {
2902
+ this.traitUtilities.set(utility.trait, utility);
2903
+ }
2904
+ /**
2905
+ * Get the total weighted resource cost of a trait at a given LOD level.
2906
+ * Collapses multi-dimensional resource cost into a single scalar
2907
+ * using platform limits as normalization weights.
2908
+ */
2909
+ getWeightedCost(traitName, lodLevel = 0, instanceCount = 1) {
2910
+ const normalized = traitName.startsWith("@") ? traitName : `@${traitName}`;
2911
+ const costs = TRAIT_RESOURCE_COSTS[normalized];
2912
+ if (!costs) return 0;
2913
+ const limits = PLATFORM_BUDGETS[this.platform] ?? {};
2914
+ const scale = this.lodScaling[Math.min(lodLevel, this.lodScaling.length - 1)] ?? 0.05;
2915
+ let weighted = 0;
2916
+ for (const [cat, cost] of Object.entries(costs)) {
2917
+ const limit = limits[cat];
2918
+ if (limit && limit > 0) {
2919
+ weighted += cost * instanceCount * scale / limit;
2920
+ }
2921
+ }
2922
+ return weighted;
2923
+ }
2924
+ /**
2925
+ * Compute value/cost ratio for a trait at a given LOD level.
2926
+ * Higher = more efficient use of resources.
2927
+ */
2928
+ getValueCostRatio(traitName, lodLevel = 0, instanceCount = 1) {
2929
+ const normalized = traitName.startsWith("@") ? traitName : `@${traitName}`;
2930
+ const util = this.traitUtilities.get(normalized);
2931
+ const utility = util?.baseUtility ?? 50;
2932
+ const cost = this.getWeightedCost(normalized, lodLevel, instanceCount);
2933
+ if (cost === 0) return utility * 100;
2934
+ return utility / cost;
2935
+ }
2936
+ // ===========================================================================
2937
+ // INTERNALS
2938
+ // ===========================================================================
2939
+ flattenToAllocations(nodes, lodLevel) {
2940
+ const allocations = [];
2941
+ const seen = /* @__PURE__ */ new Set();
2942
+ for (const node of nodes) {
2943
+ for (const trait of node.traits) {
2944
+ const normalized = trait.startsWith("@") ? trait : `@${trait}`;
2945
+ if (seen.has(normalized)) continue;
2946
+ seen.add(normalized);
2947
+ allocations.push(this.buildAllocation(normalized, lodLevel, node.count || 1));
2948
+ }
2949
+ }
2950
+ return allocations;
2951
+ }
2952
+ buildAllocation(trait, lodLevel, instanceCount) {
2953
+ const normalized = trait.startsWith("@") ? trait : `@${trait}`;
2954
+ const costs = TRAIT_RESOURCE_COSTS[normalized] ?? {};
2955
+ const scale = this.lodScaling[Math.min(lodLevel, this.lodScaling.length - 1)] ?? 0.05;
2956
+ const scaledCosts = {};
2957
+ for (const [cat, cost] of Object.entries(costs)) {
2958
+ scaledCosts[cat] = Math.ceil(cost * instanceCount * scale);
2959
+ }
2960
+ const utility = this.traitUtilities.get(normalized)?.baseUtility ?? 50;
2961
+ const weightedCost = this.getWeightedCost(normalized, lodLevel, instanceCount);
2962
+ const valueCostRatio = weightedCost > 0 ? utility / weightedCost : utility * 100;
2963
+ return {
2964
+ trait: normalized,
2965
+ included: true,
2966
+ lodLevel,
2967
+ resourceCost: scaledCosts,
2968
+ economicCost: 0,
2969
+ // Set externally if marketplace pricing applies
2970
+ utility,
2971
+ valueCostRatio
2972
+ };
2973
+ }
2974
+ isRequired(trait) {
2975
+ const normalized = trait.startsWith("@") ? trait : `@${trait}`;
2976
+ return this.traitUtilities.get(normalized)?.required ?? false;
2977
+ }
2978
+ canDropAtLOD(trait, lodLevel) {
2979
+ const normalized = trait.startsWith("@") ? trait : `@${trait}`;
2980
+ const util = this.traitUtilities.get(normalized);
2981
+ if (!util) return lodLevel >= 2;
2982
+ return lodLevel >= util.minLODLevel;
2983
+ }
2984
+ computeResourcePressure(allocations, limits) {
2985
+ const totals = {};
2986
+ for (const alloc of allocations) {
2987
+ if (!alloc.included) continue;
2988
+ for (const [cat, cost] of Object.entries(alloc.resourceCost)) {
2989
+ totals[cat] = (totals[cat] || 0) + cost;
2990
+ }
2991
+ }
2992
+ const perCategory = {};
2993
+ let maxPressure = 0;
2994
+ for (const [cat, limit] of Object.entries(limits)) {
2995
+ const used = totals[cat] || 0;
2996
+ const pressure = limit ? used / limit : 0;
2997
+ perCategory[cat] = pressure;
2998
+ if (pressure > maxPressure) maxPressure = pressure;
2999
+ }
3000
+ return { maxPressure, perCategory };
3001
+ }
3002
+ };
3003
+
3004
+ // src/economy/UsageMeter.ts
3005
+ var UsageMeter = class {
3006
+ config;
3007
+ /** All usage events keyed by agentId */
3008
+ events = /* @__PURE__ */ new Map();
3009
+ /** Free-tier consumption per agent (monthly, USDC base units) */
3010
+ freeTierUsed = /* @__PURE__ */ new Map();
3011
+ /** Current month key for free-tier reset */
3012
+ currentMonthKey;
3013
+ eventCounter = 0;
3014
+ constructor(config) {
3015
+ this.config = {
3016
+ freeTier: config?.freeTier ?? { monthlyAllowance: 5e5 },
3017
+ // $0.50 free
3018
+ defaultToolCost: config?.defaultToolCost ?? 100,
3019
+ // $0.0001
3020
+ toolCosts: config?.toolCosts ?? {},
3021
+ maxEventsPerAgent: config?.maxEventsPerAgent ?? 1e4,
3022
+ telemetry: config?.telemetry
3023
+ };
3024
+ this.currentMonthKey = this.getMonthKey(Date.now());
3025
+ }
3026
+ // ===========================================================================
3027
+ // RECORDING
3028
+ // ===========================================================================
3029
+ /**
3030
+ * Record a tool call usage event.
3031
+ */
3032
+ recordUsage(agentId, toolId, metadata) {
3033
+ this.checkMonthReset();
3034
+ const cost = this.getToolCost(toolId);
3035
+ const freeTierUsed = this.freeTierUsed.get(agentId) ?? 0;
3036
+ const freeTierRemaining = Math.max(0, this.config.freeTier.monthlyAllowance - freeTierUsed);
3037
+ const isFreeTier = cost <= freeTierRemaining;
3038
+ if (isFreeTier) {
3039
+ this.freeTierUsed.set(agentId, freeTierUsed + cost);
3040
+ }
3041
+ const event = {
3042
+ id: `usage-${++this.eventCounter}`,
3043
+ agentId,
3044
+ toolId,
3045
+ cost,
3046
+ timestamp: Date.now(),
3047
+ freeTier: isFreeTier,
3048
+ metadata
3049
+ };
3050
+ const agentEvents = this.events.get(agentId) ?? [];
3051
+ agentEvents.push(event);
3052
+ if (agentEvents.length > this.config.maxEventsPerAgent) {
3053
+ agentEvents.splice(0, agentEvents.length - this.config.maxEventsPerAgent);
3054
+ }
3055
+ this.events.set(agentId, agentEvents);
3056
+ this.emitTelemetry("usage_recorded", {
3057
+ agentId,
3058
+ toolId,
3059
+ cost,
3060
+ freeTier: isFreeTier
3061
+ });
3062
+ return event;
3063
+ }
3064
+ // ===========================================================================
3065
+ // COST LOOKUP
3066
+ // ===========================================================================
3067
+ /**
3068
+ * Get the cost for a specific tool.
3069
+ */
3070
+ getToolCost(toolId) {
3071
+ if (this.config.toolCosts[toolId] !== void 0) {
3072
+ return this.config.toolCosts[toolId];
3073
+ }
3074
+ if (this.config.freeTier.toolOverrides?.[toolId] !== void 0) {
3075
+ return this.config.freeTier.toolOverrides[toolId];
3076
+ }
3077
+ return this.config.defaultToolCost;
3078
+ }
3079
+ /**
3080
+ * Set cost for a specific tool.
3081
+ */
3082
+ setToolCost(toolId, cost) {
3083
+ this.config.toolCosts[toolId] = cost;
3084
+ }
3085
+ // ===========================================================================
3086
+ // AGGREGATION
3087
+ // ===========================================================================
3088
+ /**
3089
+ * Get usage summary for an agent within a time period.
3090
+ */
3091
+ getAgentUsage(agentId, period = "monthly") {
3092
+ const events = this.events.get(agentId) ?? [];
3093
+ const { start, end } = this.getPeriodBounds(period);
3094
+ const filtered = events.filter((e) => e.timestamp >= start && e.timestamp < end);
3095
+ const byTool = /* @__PURE__ */ new Map();
3096
+ const totalAgg = {
3097
+ totalCalls: 0,
3098
+ totalCost: 0,
3099
+ freeTierCalls: 0,
3100
+ freeTierCost: 0,
3101
+ paidCalls: 0,
3102
+ paidCost: 0,
3103
+ periodStart: new Date(start).toISOString(),
3104
+ periodEnd: new Date(end).toISOString()
3105
+ };
3106
+ for (const event of filtered) {
3107
+ let toolAgg = byTool.get(event.toolId);
3108
+ if (!toolAgg) {
3109
+ toolAgg = {
3110
+ totalCalls: 0,
3111
+ totalCost: 0,
3112
+ freeTierCalls: 0,
3113
+ freeTierCost: 0,
3114
+ paidCalls: 0,
3115
+ paidCost: 0,
3116
+ periodStart: new Date(start).toISOString(),
3117
+ periodEnd: new Date(end).toISOString()
3118
+ };
3119
+ byTool.set(event.toolId, toolAgg);
3120
+ }
3121
+ toolAgg.totalCalls++;
3122
+ toolAgg.totalCost += event.cost;
3123
+ totalAgg.totalCalls++;
3124
+ totalAgg.totalCost += event.cost;
3125
+ if (event.freeTier) {
3126
+ toolAgg.freeTierCalls++;
3127
+ toolAgg.freeTierCost += event.cost;
3128
+ totalAgg.freeTierCalls++;
3129
+ totalAgg.freeTierCost += event.cost;
3130
+ } else {
3131
+ toolAgg.paidCalls++;
3132
+ toolAgg.paidCost += event.cost;
3133
+ totalAgg.paidCalls++;
3134
+ totalAgg.paidCost += event.cost;
3135
+ }
3136
+ }
3137
+ const freeTierUsed = this.freeTierUsed.get(agentId) ?? 0;
3138
+ const freeTierRemaining = Math.max(0, this.config.freeTier.monthlyAllowance - freeTierUsed);
3139
+ return {
3140
+ agentId,
3141
+ byTool,
3142
+ total: totalAgg,
3143
+ freeTierRemaining
3144
+ };
3145
+ }
3146
+ /**
3147
+ * Get aggregated usage across all agents for a period.
3148
+ */
3149
+ getGlobalUsage(period = "monthly") {
3150
+ const { start, end } = this.getPeriodBounds(period);
3151
+ const agg = {
3152
+ totalCalls: 0,
3153
+ totalCost: 0,
3154
+ freeTierCalls: 0,
3155
+ freeTierCost: 0,
3156
+ paidCalls: 0,
3157
+ paidCost: 0,
3158
+ periodStart: new Date(start).toISOString(),
3159
+ periodEnd: new Date(end).toISOString()
3160
+ };
3161
+ for (const events of this.events.values()) {
3162
+ for (const event of events) {
3163
+ if (event.timestamp >= start && event.timestamp < end) {
3164
+ agg.totalCalls++;
3165
+ agg.totalCost += event.cost;
3166
+ if (event.freeTier) {
3167
+ agg.freeTierCalls++;
3168
+ agg.freeTierCost += event.cost;
3169
+ } else {
3170
+ agg.paidCalls++;
3171
+ agg.paidCost += event.cost;
3172
+ }
3173
+ }
3174
+ }
3175
+ }
3176
+ return agg;
3177
+ }
3178
+ /**
3179
+ * Get top tools by usage cost.
3180
+ */
3181
+ getTopTools(period = "monthly", limit = 10) {
3182
+ const { start, end } = this.getPeriodBounds(period);
3183
+ const toolMap = /* @__PURE__ */ new Map();
3184
+ for (const events of this.events.values()) {
3185
+ for (const event of events) {
3186
+ if (event.timestamp >= start && event.timestamp < end) {
3187
+ const existing = toolMap.get(event.toolId) ?? { calls: 0, cost: 0 };
3188
+ existing.calls++;
3189
+ existing.cost += event.cost;
3190
+ toolMap.set(event.toolId, existing);
3191
+ }
3192
+ }
3193
+ }
3194
+ return [...toolMap.entries()].map(([toolId, data]) => ({ toolId, ...data })).sort((a, b) => b.cost - a.cost).slice(0, limit);
3195
+ }
3196
+ // ===========================================================================
3197
+ // FREE TIER
3198
+ // ===========================================================================
3199
+ /**
3200
+ * Get free-tier remaining for an agent.
3201
+ */
3202
+ getFreeTierRemaining(agentId) {
3203
+ this.checkMonthReset();
3204
+ const used = this.freeTierUsed.get(agentId) ?? 0;
3205
+ return Math.max(0, this.config.freeTier.monthlyAllowance - used);
3206
+ }
3207
+ /**
3208
+ * Check if an agent has exceeded their free tier.
3209
+ */
3210
+ isOverFreeTier(agentId) {
3211
+ return this.getFreeTierRemaining(agentId) === 0;
3212
+ }
3213
+ // ===========================================================================
3214
+ // QUERIES
3215
+ // ===========================================================================
3216
+ /**
3217
+ * Get all tracked agent IDs.
3218
+ */
3219
+ getTrackedAgents() {
3220
+ return [...this.events.keys()];
3221
+ }
3222
+ /**
3223
+ * Get raw events for an agent.
3224
+ */
3225
+ getEvents(agentId) {
3226
+ return [...this.events.get(agentId) ?? []];
3227
+ }
3228
+ /**
3229
+ * Get total number of recorded events.
3230
+ */
3231
+ getTotalEventCount() {
3232
+ let total = 0;
3233
+ for (const events of this.events.values()) {
3234
+ total += events.length;
3235
+ }
3236
+ return total;
3237
+ }
3238
+ // ===========================================================================
3239
+ // INTERNALS
3240
+ // ===========================================================================
3241
+ getMonthKey(timestamp) {
3242
+ const d = new Date(timestamp);
3243
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`;
3244
+ }
3245
+ checkMonthReset() {
3246
+ const currentKey = this.getMonthKey(Date.now());
3247
+ if (currentKey !== this.currentMonthKey) {
3248
+ this.freeTierUsed.clear();
3249
+ this.currentMonthKey = currentKey;
3250
+ }
3251
+ }
3252
+ getPeriodBounds(period) {
3253
+ const now = /* @__PURE__ */ new Date();
3254
+ let start;
3255
+ switch (period) {
3256
+ case "hourly":
3257
+ start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours());
3258
+ return { start: start.getTime(), end: start.getTime() + 36e5 };
3259
+ case "daily":
3260
+ start = new Date(now.getFullYear(), now.getMonth(), now.getDate());
3261
+ return { start: start.getTime(), end: start.getTime() + 864e5 };
3262
+ case "monthly":
3263
+ start = new Date(now.getFullYear(), now.getMonth(), 1);
3264
+ const nextMonth = new Date(now.getFullYear(), now.getMonth() + 1, 1);
3265
+ return { start: start.getTime(), end: nextMonth.getTime() };
3266
+ }
3267
+ }
3268
+ emitTelemetry(type, data) {
3269
+ this.config.telemetry?.record({
3270
+ type,
3271
+ severity: "info",
3272
+ agentId: "usage-meter",
3273
+ data
3274
+ });
3275
+ }
3276
+ };
3277
+
3278
+ // src/economy/BountyManager.ts
3279
+ var BountyManager = class {
3280
+ bounties = /* @__PURE__ */ new Map();
3281
+ nextId = 1;
3282
+ config;
3283
+ constructor(config = {}) {
3284
+ this.config = config;
3285
+ }
3286
+ /** Create a bounty for a board task. */
3287
+ createBounty(taskId, reward, createdBy, deadline) {
3288
+ if (reward.amount <= 0) throw new Error("Bounty reward must be positive");
3289
+ const id = `bounty_${String(this.nextId++).padStart(4, "0")}`;
3290
+ const now = Date.now();
3291
+ const bounty = {
3292
+ id,
3293
+ taskId,
3294
+ reward,
3295
+ status: "open",
3296
+ createdBy,
3297
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3298
+ deadline: deadline ?? (this.config.defaultDeadlineMs ? now + this.config.defaultDeadlineMs : void 0)
3299
+ };
3300
+ this.bounties.set(id, bounty);
3301
+ return bounty;
3302
+ }
3303
+ /** Claim an open bounty. */
3304
+ claimBounty(bountyId, agentId) {
3305
+ const bounty = this.bounties.get(bountyId);
3306
+ if (!bounty) return { success: false, bountyId, error: "Bounty not found" };
3307
+ if (bounty.status !== "open")
3308
+ return { success: false, bountyId, error: `Bounty is ${bounty.status}, not open` };
3309
+ if (bounty.deadline && Date.now() > bounty.deadline) {
3310
+ bounty.status = "expired";
3311
+ return { success: false, bountyId, error: "Bounty has expired" };
3312
+ }
3313
+ bounty.status = "claimed";
3314
+ bounty.claimedBy = agentId;
3315
+ return { success: true, bountyId };
3316
+ }
3317
+ /** Complete a bounty with proof of work and trigger payout. */
3318
+ completeBounty(bountyId, proof) {
3319
+ const bounty = this.bounties.get(bountyId);
3320
+ if (!bounty)
3321
+ return {
3322
+ success: false,
3323
+ bountyId,
3324
+ amount: 0,
3325
+ currency: "credits",
3326
+ settlement: "ledger",
3327
+ error: "Bounty not found"
3328
+ };
3329
+ if (bounty.status !== "claimed")
3330
+ return {
3331
+ success: false,
3332
+ bountyId,
3333
+ amount: 0,
3334
+ currency: bounty.reward.currency,
3335
+ settlement: "ledger",
3336
+ error: `Bounty is ${bounty.status}, not claimed`
3337
+ };
3338
+ if (!proof.summary || proof.summary.trim().length === 0) {
3339
+ return {
3340
+ success: false,
3341
+ bountyId,
3342
+ amount: 0,
3343
+ currency: bounty.reward.currency,
3344
+ settlement: "ledger",
3345
+ error: "Completion proof requires a summary"
3346
+ };
3347
+ }
3348
+ bounty.status = "completed";
3349
+ bounty.completedAt = (/* @__PURE__ */ new Date()).toISOString();
3350
+ const settlement = bounty.reward.currency === "credits" ? "ledger" : bounty.reward.amount < 0.1 ? "ledger" : "on_chain";
3351
+ return {
3352
+ success: true,
3353
+ bountyId,
3354
+ amount: bounty.reward.amount,
3355
+ currency: bounty.reward.currency,
3356
+ settlement
3357
+ };
3358
+ }
3359
+ /** Get a bounty by ID. */
3360
+ getBounty(bountyId) {
3361
+ return this.bounties.get(bountyId);
3362
+ }
3363
+ /** List bounties, optionally filtered by status. */
3364
+ list(status) {
3365
+ const all = Array.from(this.bounties.values());
3366
+ if (!status) return all;
3367
+ return all.filter((b) => b.status === status);
3368
+ }
3369
+ /** List bounties for a specific task. */
3370
+ byTask(taskId) {
3371
+ return Array.from(this.bounties.values()).filter((b) => b.taskId === taskId);
3372
+ }
3373
+ /** Expire bounties past their deadline. Returns count expired. */
3374
+ expireStale() {
3375
+ const now = Date.now();
3376
+ let count = 0;
3377
+ for (const bounty of this.bounties.values()) {
3378
+ if (bounty.deadline && now > bounty.deadline && bounty.status === "open") {
3379
+ bounty.status = "expired";
3380
+ count++;
3381
+ }
3382
+ }
3383
+ return count;
3384
+ }
3385
+ /** Total open bounty value in a given currency. */
3386
+ totalOpen(currency) {
3387
+ return this.list("open").filter((b) => !currency || b.reward.currency === currency).reduce((sum, b) => sum + b.reward.amount, 0);
3388
+ }
3389
+ };
3390
+
3391
+ // src/economy/KnowledgeMarketplace.ts
3392
+ var DEFAULT_TYPE_WEIGHTS = {
3393
+ wisdom: 0.05,
3394
+ pattern: 0.03,
3395
+ gotcha: 0.02
3396
+ };
3397
+ var KnowledgeMarketplace = class {
3398
+ listings = /* @__PURE__ */ new Map();
3399
+ purchases = /* @__PURE__ */ new Map();
3400
+ // buyer -> purchases
3401
+ nextId = 1;
3402
+ pricingFactors;
3403
+ constructor(pricingFactors) {
3404
+ this.pricingFactors = pricingFactors ?? {};
3405
+ }
3406
+ /** Estimate the value of a knowledge entry (in USDC). */
3407
+ priceKnowledge(entry) {
3408
+ const weights = this.pricingFactors.typeWeights ?? DEFAULT_TYPE_WEIGHTS;
3409
+ let price = weights[entry.type] ?? 0.02;
3410
+ if (entry.confidence >= 0.8) {
3411
+ price *= this.pricingFactors.confidenceMultiplier ?? 1.5;
3412
+ }
3413
+ if (entry.reuseCount >= 5) {
3414
+ price *= this.pricingFactors.reuseMultiplier ?? 2;
3415
+ }
3416
+ if (entry.queryCount >= 10) {
3417
+ price *= 1.25;
3418
+ }
3419
+ return Math.round(price * 1e4) / 1e4;
3420
+ }
3421
+ /** List a knowledge entry for sale. */
3422
+ sellKnowledge(entry, price, seller, currency = "USDC") {
3423
+ if (price <= 0) return { success: false, listingId: "", error: "Price must be positive" };
3424
+ for (const listing2 of this.listings.values()) {
3425
+ if (listing2.entryId === entry.id && listing2.status === "active") {
3426
+ return { success: false, listingId: listing2.id, error: "Entry already listed" };
3427
+ }
3428
+ }
3429
+ const id = `listing_${String(this.nextId++).padStart(4, "0")}`;
3430
+ const listing = {
3431
+ id,
3432
+ entryId: entry.id,
3433
+ seller,
3434
+ price,
3435
+ currency,
3436
+ status: "active",
3437
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3438
+ preview: {
3439
+ type: entry.type,
3440
+ domain: entry.domain,
3441
+ snippet: entry.content.slice(0, 100)
3442
+ }
3443
+ };
3444
+ this.listings.set(id, listing);
3445
+ return { success: true, listingId: id };
3446
+ }
3447
+ /** Buy a listed knowledge entry. */
3448
+ buyKnowledge(listingId, buyer) {
3449
+ const listing = this.listings.get(listingId);
3450
+ if (!listing) return { success: false, listingId, buyer, price: 0, error: "Listing not found" };
3451
+ if (listing.status !== "active")
3452
+ return {
3453
+ success: false,
3454
+ listingId,
3455
+ buyer,
3456
+ price: listing.price,
3457
+ error: `Listing is ${listing.status}`
3458
+ };
3459
+ if (listing.seller === buyer)
3460
+ return {
3461
+ success: false,
3462
+ listingId,
3463
+ buyer,
3464
+ price: listing.price,
3465
+ error: "Cannot buy your own listing"
3466
+ };
3467
+ listing.status = "sold";
3468
+ const result = {
3469
+ success: true,
3470
+ listingId,
3471
+ entryId: listing.entryId,
3472
+ buyer,
3473
+ price: listing.price
3474
+ };
3475
+ const buyerPurchases = this.purchases.get(buyer) ?? [];
3476
+ buyerPurchases.push(result);
3477
+ this.purchases.set(buyer, buyerPurchases);
3478
+ return result;
3479
+ }
3480
+ /** Get a listing by ID. */
3481
+ getListing(listingId) {
3482
+ return this.listings.get(listingId);
3483
+ }
3484
+ /** List all active listings. */
3485
+ activeListings() {
3486
+ return Array.from(this.listings.values()).filter((l) => l.status === "active");
3487
+ }
3488
+ /** Get purchase history for a buyer. */
3489
+ purchaseHistory(buyer) {
3490
+ return this.purchases.get(buyer) ?? [];
3491
+ }
3492
+ /** Delist an entry (only the seller can delist). */
3493
+ delist(listingId, seller) {
3494
+ const listing = this.listings.get(listingId);
3495
+ if (!listing || listing.seller !== seller || listing.status !== "active") return false;
3496
+ listing.status = "delisted";
3497
+ return true;
3498
+ }
3499
+ /** Total revenue for a seller across all sold listings. */
3500
+ sellerRevenue(seller) {
3501
+ return Array.from(this.listings.values()).filter((l) => l.seller === seller && l.status === "sold").reduce((sum, l) => sum + l.price, 0);
3502
+ }
3503
+ /** Total marketplace volume (all completed sales). */
3504
+ totalVolume() {
3505
+ return Array.from(this.listings.values()).filter((l) => l.status === "sold").reduce((sum, l) => sum + l.price, 0);
3506
+ }
3507
+ };
3508
+
3509
+ // src/economy/RevenueSplitter.ts
3510
+ var TOTAL_BASIS_POINTS = 1e4;
3511
+ var RevenueSplitter = class {
3512
+ recipients;
3513
+ /**
3514
+ * Create a revenue splitter.
3515
+ *
3516
+ * @param recipients — Array of recipients with basis points.
3517
+ * Basis points must sum to exactly 10000 (100%).
3518
+ * @throws Error if basis points don't sum to 10000 or any are negative.
3519
+ */
3520
+ constructor(recipients) {
3521
+ if (recipients.length === 0) {
3522
+ throw new Error("At least one recipient is required");
3523
+ }
3524
+ const sum = recipients.reduce((acc, r) => acc + r.basisPoints, 0);
3525
+ if (sum !== TOTAL_BASIS_POINTS) {
3526
+ throw new Error(`Basis points must sum to ${TOTAL_BASIS_POINTS} (got ${sum})`);
3527
+ }
3528
+ for (const r of recipients) {
3529
+ if (r.basisPoints < 0) {
3530
+ throw new Error(`Negative basis points for "${r.id}": ${r.basisPoints}`);
3531
+ }
3532
+ }
3533
+ const ids = new Set(recipients.map((r) => r.id));
3534
+ if (ids.size !== recipients.length) {
3535
+ throw new Error("Duplicate recipient IDs");
3536
+ }
3537
+ this.recipients = [...recipients];
3538
+ }
3539
+ /**
3540
+ * Split an amount among recipients.
3541
+ *
3542
+ * Uses bigint arithmetic for exact splitting.
3543
+ * Dust (remainder from integer division) goes to the first recipient.
3544
+ *
3545
+ * @param totalAmount — Total amount to split (in base units, e.g., USDC 6 decimals)
3546
+ * @returns SplitResult with exact shares summing to totalAmount
3547
+ */
3548
+ split(totalAmount) {
3549
+ if (totalAmount < 0n) {
3550
+ throw new Error("Cannot split negative amount");
3551
+ }
3552
+ const shares = /* @__PURE__ */ new Map();
3553
+ const breakdown = [];
3554
+ let allocated = 0n;
3555
+ for (const recipient of this.recipients) {
3556
+ const share = totalAmount * BigInt(recipient.basisPoints) / BigInt(TOTAL_BASIS_POINTS);
3557
+ shares.set(recipient.id, share);
3558
+ allocated += share;
3559
+ breakdown.push({
3560
+ recipientId: recipient.id,
3561
+ basisPoints: recipient.basisPoints,
3562
+ amount: share,
3563
+ percentage: `${(recipient.basisPoints / 100).toFixed(2)}%`
3564
+ });
3565
+ }
3566
+ const dust = totalAmount - allocated;
3567
+ if (dust > 0n) {
3568
+ const firstId = this.recipients[0].id;
3569
+ const current = shares.get(firstId);
3570
+ shares.set(firstId, current + dust);
3571
+ breakdown[0].amount = current + dust;
3572
+ }
3573
+ return { shares, total: totalAmount, dust, breakdown };
3574
+ }
3575
+ /**
3576
+ * Split a numeric amount (convenience wrapper).
3577
+ * Converts to bigint internally.
3578
+ */
3579
+ splitNumeric(totalAmount) {
3580
+ return this.split(BigInt(Math.floor(totalAmount)));
3581
+ }
3582
+ /**
3583
+ * Get the configured recipients.
3584
+ */
3585
+ getRecipients() {
3586
+ return this.recipients;
3587
+ }
3588
+ /**
3589
+ * Validate that a split result sums correctly.
3590
+ */
3591
+ static validate(result) {
3592
+ let sum = 0n;
3593
+ for (const amount of result.shares.values()) {
3594
+ sum += amount;
3595
+ }
3596
+ return sum === result.total;
3597
+ }
3598
+ };
3599
+
3600
+ // src/economy/InvisibleWallet.ts
3601
+ var InvisibleWalletStub = class _InvisibleWalletStub {
3602
+ address;
3603
+ chainId;
3604
+ isTestnet;
3605
+ constructor(address, config = {}) {
3606
+ this.address = address;
3607
+ this.isTestnet = config.testnet ?? false;
3608
+ this.chainId = this.isTestnet ? 84531 : 8453;
3609
+ }
3610
+ /**
3611
+ * Create from an address string (no private key needed for read-only).
3612
+ */
3613
+ static fromAddress(address, config = {}) {
3614
+ const hex = address.startsWith("0x") ? address : `0x${address}`;
3615
+ return new _InvisibleWalletStub(hex, config);
3616
+ }
3617
+ /** Get the wallet address */
3618
+ getAddress() {
3619
+ return this.address;
3620
+ }
3621
+ /** Get chain ID */
3622
+ getChainId() {
3623
+ return this.chainId;
3624
+ }
3625
+ /** Get wallet info */
3626
+ getInfo() {
3627
+ return {
3628
+ address: this.address,
3629
+ chainId: this.chainId,
3630
+ isTestnet: this.isTestnet
3631
+ };
3632
+ }
3633
+ };
3634
+ // Annotate the CommonJS export names for ESM import in node:
3635
+ 0 && (module.exports = {
3636
+ AgentBudgetEnforcer,
3637
+ BountyManager,
3638
+ CHAIN_IDS,
3639
+ CHAIN_ID_TO_NETWORK,
3640
+ CreatorRevenueAggregator,
3641
+ DEFAULT_COST_FLOOR,
3642
+ DEFAULT_LOD_SCALING,
3643
+ DEFAULT_TRAIT_UTILITIES,
3644
+ InvisibleWalletStub,
3645
+ KnowledgeMarketplace,
3646
+ MICRO_PAYMENT_THRESHOLD,
3647
+ MicroPaymentLedger,
3648
+ PLATFORM_LOD_SCALING,
3649
+ PaymentGateway,
3650
+ PaymentWebhookService,
3651
+ RevenueSplitter,
3652
+ SubscriptionManager,
3653
+ USDC_CONTRACTS,
3654
+ UnifiedBudgetOptimizer,
3655
+ UsageMeter,
3656
+ X402Facilitator,
3657
+ X402_VERSION,
3658
+ creditTraitHandler
3659
+ });