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