@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.
- package/ALL-test-results.json +1 -0
- package/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/ROADMAP.md +175 -0
- package/dist/AgentManifest-CB4xM-Ma.d.cts +704 -0
- package/dist/AgentManifest-CB4xM-Ma.d.ts +704 -0
- package/dist/BehaviorTree-BrBFECv5.d.cts +103 -0
- package/dist/BehaviorTree-BrBFECv5.d.ts +103 -0
- package/dist/InvisibleWallet-BB6tFvRA.d.cts +1732 -0
- package/dist/InvisibleWallet-rtRrBOA8.d.ts +1732 -0
- package/dist/OrchestratorAgent-BvWgf9uw.d.cts +798 -0
- package/dist/OrchestratorAgent-Q_CbVTmO.d.ts +798 -0
- package/dist/agents/index.cjs +4790 -0
- package/dist/agents/index.d.cts +1788 -0
- package/dist/agents/index.d.ts +1788 -0
- package/dist/agents/index.js +4695 -0
- package/dist/ai/index.cjs +5347 -0
- package/dist/ai/index.d.cts +1753 -0
- package/dist/ai/index.d.ts +1753 -0
- package/dist/ai/index.js +5244 -0
- package/dist/behavior.cjs +449 -0
- package/dist/behavior.d.cts +130 -0
- package/dist/behavior.d.ts +130 -0
- package/dist/behavior.js +407 -0
- package/dist/economy/index.cjs +3659 -0
- package/dist/economy/index.d.cts +747 -0
- package/dist/economy/index.d.ts +747 -0
- package/dist/economy/index.js +3617 -0
- package/dist/implementations-D9T3un9D.d.cts +236 -0
- package/dist/implementations-D9T3un9D.d.ts +236 -0
- package/dist/index.cjs +24550 -0
- package/dist/index.d.cts +1729 -0
- package/dist/index.d.ts +1729 -0
- package/dist/index.js +24277 -0
- package/dist/learning/index.cjs +219 -0
- package/dist/learning/index.d.cts +104 -0
- package/dist/learning/index.d.ts +104 -0
- package/dist/learning/index.js +189 -0
- package/dist/negotiation/index.cjs +970 -0
- package/dist/negotiation/index.d.cts +610 -0
- package/dist/negotiation/index.d.ts +610 -0
- package/dist/negotiation/index.js +931 -0
- package/dist/skills/index.cjs +1118 -0
- package/dist/skills/index.d.cts +289 -0
- package/dist/skills/index.d.ts +289 -0
- package/dist/skills/index.js +1079 -0
- package/dist/swarm/index.cjs +5268 -0
- package/dist/swarm/index.d.cts +2433 -0
- package/dist/swarm/index.d.ts +2433 -0
- package/dist/swarm/index.js +5221 -0
- package/dist/training/index.cjs +2745 -0
- package/dist/training/index.d.cts +1734 -0
- package/dist/training/index.d.ts +1734 -0
- package/dist/training/index.js +2687 -0
- package/extract-failures.js +10 -0
- package/package.json +82 -0
- package/src/__tests__/bounty-marketplace.test.ts +374 -0
- package/src/__tests__/delegation.test.ts +144 -0
- package/src/__tests__/distributed-claimer.test.ts +147 -0
- package/src/__tests__/done-log-audit.test.ts +342 -0
- package/src/__tests__/framework.test.ts +865 -0
- package/src/__tests__/goal-synthesizer.test.ts +236 -0
- package/src/__tests__/presence.test.ts +223 -0
- package/src/__tests__/protocol-agent.test.ts +254 -0
- package/src/__tests__/revenue-splitter.test.ts +114 -0
- package/src/__tests__/scenario-driven-todo.test.ts +197 -0
- package/src/__tests__/self-improve.test.ts +349 -0
- package/src/__tests__/service-lifecycle.test.ts +237 -0
- package/src/__tests__/skill-router.test.ts +121 -0
- package/src/agents/AgentManifest.ts +493 -0
- package/src/agents/AgentRegistry.ts +475 -0
- package/src/agents/AgentTypes.ts +585 -0
- package/src/agents/AgentWalletRegistry.ts +83 -0
- package/src/agents/AuthenticatedCRDT.ts +388 -0
- package/src/agents/CapabilityMatcher.ts +453 -0
- package/src/agents/CrossRealityHandoff.ts +305 -0
- package/src/agents/CulturalMemory.ts +454 -0
- package/src/agents/FederatedRegistryAdapter.ts +429 -0
- package/src/agents/NormEngine.ts +450 -0
- package/src/agents/OrchestratorAgent.ts +414 -0
- package/src/agents/SkillWorkflowEngine.ts +472 -0
- package/src/agents/TaskDelegationService.ts +551 -0
- package/src/agents/__tests__/AgentManifest.prod.test.ts +134 -0
- package/src/agents/__tests__/AgentManifest.test.ts +182 -0
- package/src/agents/__tests__/AgentModule.test.ts +864 -0
- package/src/agents/__tests__/AgentRegistry.prod.test.ts +125 -0
- package/src/agents/__tests__/AgentRegistry.test.ts +148 -0
- package/src/agents/__tests__/AgentTypes.test.ts +534 -0
- package/src/agents/__tests__/AgentWalletRegistry.test.ts +152 -0
- package/src/agents/__tests__/AuthenticatedCRDT.test.ts +558 -0
- package/src/agents/__tests__/CapabilityMatcher.prod.test.ts +117 -0
- package/src/agents/__tests__/CapabilityMatcher.test.ts +178 -0
- package/src/agents/__tests__/CrossRealityHandoff.test.ts +402 -0
- package/src/agents/__tests__/CulturalMemory.test.ts +200 -0
- package/src/agents/__tests__/FederatedRegistryAdapter.test.ts +409 -0
- package/src/agents/__tests__/NormEngine.test.ts +276 -0
- package/src/agents/__tests__/OrchestratorAgent.test.ts +182 -0
- package/src/agents/__tests__/SkillWorkflowEngine.test.ts +357 -0
- package/src/agents/__tests__/TaskDelegationService.test.ts +446 -0
- package/src/agents/index.ts +107 -0
- package/src/agents/spatial-comms/Layer1RealTime.ts +621 -0
- package/src/agents/spatial-comms/Layer2A2A.ts +661 -0
- package/src/agents/spatial-comms/Layer3MCP.ts +651 -0
- package/src/agents/spatial-comms/ProtocolTypes.ts +543 -0
- package/src/agents/spatial-comms/SpatialCommClient.ts +483 -0
- package/src/agents/spatial-comms/__tests__/performance-benchmark.test.ts +465 -0
- package/src/agents/spatial-comms/examples/multi-agent-world-creation.ts +409 -0
- package/src/agents/spatial-comms/index.ts +66 -0
- package/src/ai/AIAdapter.ts +313 -0
- package/src/ai/AICopilot.ts +331 -0
- package/src/ai/AIOutputValidator.ts +203 -0
- package/src/ai/BTNodes.ts +239 -0
- package/src/ai/BehaviorSelector.ts +135 -0
- package/src/ai/BehaviorTree.ts +153 -0
- package/src/ai/Blackboard.ts +165 -0
- package/src/ai/GenerationAnalytics.ts +461 -0
- package/src/ai/GenerationCache.ts +265 -0
- package/src/ai/GoalPlanner.ts +165 -0
- package/src/ai/HoloScriptGenerator.ts +580 -0
- package/src/ai/InfluenceMap.ts +180 -0
- package/src/ai/NavMesh.ts +168 -0
- package/src/ai/PerceptionSystem.ts +178 -0
- package/src/ai/PromptTemplates.ts +453 -0
- package/src/ai/SemanticSearchService.ts +80 -0
- package/src/ai/StateMachine.ts +196 -0
- package/src/ai/SteeringBehavior.ts +150 -0
- package/src/ai/SteeringBehaviors.ts +244 -0
- package/src/ai/TrainingDataGenerator.ts +1082 -0
- package/src/ai/UtilityAI.ts +145 -0
- package/src/ai/__tests__/AIAdapter.prod.test.ts +259 -0
- package/src/ai/__tests__/AIAdapter.test.ts +109 -0
- package/src/ai/__tests__/AICopilot.prod.test.ts +341 -0
- package/src/ai/__tests__/AICopilot.test.ts +178 -0
- package/src/ai/__tests__/AIOutputValidator.prod.test.ts +226 -0
- package/src/ai/__tests__/AIOutputValidator.test.ts +138 -0
- package/src/ai/__tests__/BTNodes.prod.test.ts +391 -0
- package/src/ai/__tests__/BTNodes.test.ts +263 -0
- package/src/ai/__tests__/BehaviorSelector.prod.test.ts +129 -0
- package/src/ai/__tests__/BehaviorSelector.test.ts +132 -0
- package/src/ai/__tests__/BehaviorTree.prod.test.ts +266 -0
- package/src/ai/__tests__/BehaviorTree.test.ts +216 -0
- package/src/ai/__tests__/Blackboard.prod.test.ts +339 -0
- package/src/ai/__tests__/Blackboard.test.ts +183 -0
- package/src/ai/__tests__/GenerationAnalytics.prod.test.ts +141 -0
- package/src/ai/__tests__/GenerationAnalytics.test.ts +165 -0
- package/src/ai/__tests__/GenerationCache.prod.test.ts +144 -0
- package/src/ai/__tests__/GenerationCache.test.ts +171 -0
- package/src/ai/__tests__/GoalPlanner.prod.test.ts +189 -0
- package/src/ai/__tests__/GoalPlanner.test.ts +137 -0
- package/src/ai/__tests__/GoalPlannerDepth.prod.test.ts +217 -0
- package/src/ai/__tests__/HoloScriptGenerator.test.ts +125 -0
- package/src/ai/__tests__/InfluenceMap.prod.test.ts +146 -0
- package/src/ai/__tests__/InfluenceMap.test.ts +149 -0
- package/src/ai/__tests__/NavMesh.prod.test.ts +141 -0
- package/src/ai/__tests__/NavMesh.test.ts +159 -0
- package/src/ai/__tests__/PerceptionSystem.prod.test.ts +135 -0
- package/src/ai/__tests__/PerceptionSystem.test.ts +250 -0
- package/src/ai/__tests__/PromptTemplates.prod.test.ts +313 -0
- package/src/ai/__tests__/PromptTemplates.test.ts +146 -0
- package/src/ai/__tests__/SemanticSearch.test.ts +37 -0
- package/src/ai/__tests__/StateMachine.prod.test.ts +162 -0
- package/src/ai/__tests__/StateMachine.test.ts +163 -0
- package/src/ai/__tests__/SteeringBehavior.prod.test.ts +251 -0
- package/src/ai/__tests__/SteeringBehavior.test.ts +135 -0
- package/src/ai/__tests__/SteeringBehaviors.prod.test.ts +133 -0
- package/src/ai/__tests__/SteeringBehaviors.test.ts +151 -0
- package/src/ai/__tests__/TrainingDataGenerator.prod.test.ts +286 -0
- package/src/ai/__tests__/TrainingDataGenerator.test.ts +286 -0
- package/src/ai/__tests__/UtilityAI.prod.test.ts +207 -0
- package/src/ai/__tests__/UtilityAI.test.ts +155 -0
- package/src/ai/__tests__/adapters.prod.test.ts +263 -0
- package/src/ai/__tests__/adapters.test.ts +320 -0
- package/src/ai/adapters.ts +1585 -0
- package/src/ai/index.ts +130 -0
- package/src/behavior/BehaviorPresets.ts +140 -0
- package/src/behavior/BehaviorTree.ts +236 -0
- package/src/behavior/StateMachine.ts +176 -0
- package/src/behavior/StateTrait.ts +67 -0
- package/src/behavior/index.ts +8 -0
- package/src/behavior.ts +8 -0
- package/src/board/audit.ts +284 -0
- package/src/board/board-ops.ts +336 -0
- package/src/board/board-types.ts +302 -0
- package/src/board/index.ts +69 -0
- package/src/define-agent.ts +46 -0
- package/src/define-team.ts +33 -0
- package/src/delegation.ts +265 -0
- package/src/distributed-claimer.ts +228 -0
- package/src/economy/AgentBudgetEnforcer.ts +464 -0
- package/src/economy/BountyManager.ts +185 -0
- package/src/economy/CreatorRevenueAggregator.ts +460 -0
- package/src/economy/InvisibleWallet.ts +82 -0
- package/src/economy/KnowledgeMarketplace.ts +193 -0
- package/src/economy/PaymentWebhookService.ts +512 -0
- package/src/economy/RevenueSplitter.ts +156 -0
- package/src/economy/SubscriptionManager.ts +546 -0
- package/src/economy/UnifiedBudgetOptimizer.ts +635 -0
- package/src/economy/UsageMeter.ts +440 -0
- package/src/economy/_core-stubs.ts +219 -0
- package/src/economy/index.ts +100 -0
- package/src/economy/x402-facilitator.ts +1978 -0
- package/src/index.ts +348 -0
- package/src/knowledge/__tests__/knowledge-consolidator.test.ts +444 -0
- package/src/knowledge/__tests__/knowledge-store-vector.test.ts +291 -0
- package/src/knowledge/brain.ts +167 -0
- package/src/knowledge/consolidation.ts +581 -0
- package/src/knowledge/knowledge-consolidator.ts +510 -0
- package/src/knowledge/knowledge-store.ts +616 -0
- package/src/learning/MemoryConsolidator.ts +102 -0
- package/src/learning/MemoryScorer.ts +69 -0
- package/src/learning/ProceduralCompiler.ts +45 -0
- package/src/learning/SemanticClusterer.ts +66 -0
- package/src/learning/index.ts +8 -0
- package/src/llm/llm-adapter.ts +159 -0
- package/src/mesh/index.ts +309 -0
- package/src/negotiation/NegotiationProtocol.ts +694 -0
- package/src/negotiation/NegotiationTypes.ts +473 -0
- package/src/negotiation/VotingMechanisms.ts +691 -0
- package/src/negotiation/index.ts +49 -0
- package/src/protocol/goal-synthesizer.ts +317 -0
- package/src/protocol/implementations.ts +474 -0
- package/src/protocol/micro-phase-decomposer.ts +299 -0
- package/src/protocol/micro-step-decomposer.test.ts +306 -0
- package/src/protocol-agent.test.ts +353 -0
- package/src/protocol-agent.ts +670 -0
- package/src/self-improve/absorb-scanner.ts +252 -0
- package/src/self-improve/evolution-engine.ts +149 -0
- package/src/self-improve/framework-absorber.ts +214 -0
- package/src/self-improve/index.ts +50 -0
- package/src/self-improve/prompt-optimizer.ts +212 -0
- package/src/self-improve/test-generator.ts +175 -0
- package/src/skill-router.ts +186 -0
- package/src/skills/index.ts +5 -0
- package/src/skills/skill-md-bridge.ts +1699 -0
- package/src/swarm/ACOEngine.ts +261 -0
- package/src/swarm/CollectiveIntelligence.ts +383 -0
- package/src/swarm/ContributionSynthesizer.ts +481 -0
- package/src/swarm/LeaderElection.ts +393 -0
- package/src/swarm/PSOEngine.ts +206 -0
- package/src/swarm/QuorumPolicy.ts +173 -0
- package/src/swarm/SwarmCoordinator.ts +335 -0
- package/src/swarm/SwarmManager.ts +442 -0
- package/src/swarm/SwarmMembership.ts +456 -0
- package/src/swarm/VotingRound.ts +255 -0
- package/src/swarm/__tests__/ACOEngine.prod.test.ts +164 -0
- package/src/swarm/__tests__/ACOEngine.test.ts +117 -0
- package/src/swarm/__tests__/CollectiveIntelligence.prod.test.ts +296 -0
- package/src/swarm/__tests__/CollectiveIntelligence.test.ts +457 -0
- package/src/swarm/__tests__/ContributionSynthesizer.prod.test.ts +269 -0
- package/src/swarm/__tests__/ContributionSynthesizer.test.ts +254 -0
- package/src/swarm/__tests__/LeaderElection.prod.test.ts +196 -0
- package/src/swarm/__tests__/LeaderElection.test.ts +151 -0
- package/src/swarm/__tests__/PSOEngine.prod.test.ts +162 -0
- package/src/swarm/__tests__/PSOEngine.test.ts +106 -0
- package/src/swarm/__tests__/QuorumPolicy.prod.test.ts +216 -0
- package/src/swarm/__tests__/QuorumPolicy.test.ts +177 -0
- package/src/swarm/__tests__/SwarmCoordinator.prod.test.ts +186 -0
- package/src/swarm/__tests__/SwarmCoordinator.test.ts +167 -0
- package/src/swarm/__tests__/SwarmManager.prod.test.ts +308 -0
- package/src/swarm/__tests__/SwarmManager.test.ts +373 -0
- package/src/swarm/__tests__/SwarmMembership.prod.test.ts +273 -0
- package/src/swarm/__tests__/SwarmMembership.test.ts +264 -0
- package/src/swarm/__tests__/VotingRound.prod.test.ts +233 -0
- package/src/swarm/__tests__/VotingRound.test.ts +174 -0
- package/src/swarm/analytics/SwarmInspector.ts +476 -0
- package/src/swarm/analytics/SwarmMetrics.ts +449 -0
- package/src/swarm/analytics/__tests__/SwarmInspector.prod.test.ts +366 -0
- package/src/swarm/analytics/__tests__/SwarmInspector.test.ts +454 -0
- package/src/swarm/analytics/__tests__/SwarmMetrics.prod.test.ts +254 -0
- package/src/swarm/analytics/__tests__/SwarmMetrics.test.ts +370 -0
- package/src/swarm/analytics/index.ts +7 -0
- package/src/swarm/index.ts +69 -0
- package/src/swarm/messaging/BroadcastChannel.ts +509 -0
- package/src/swarm/messaging/GossipProtocol.ts +565 -0
- package/src/swarm/messaging/SwarmEventBus.ts +443 -0
- package/src/swarm/messaging/__tests__/BroadcastChannel.prod.test.ts +331 -0
- package/src/swarm/messaging/__tests__/BroadcastChannel.test.ts +333 -0
- package/src/swarm/messaging/__tests__/GossipProtocol.prod.test.ts +356 -0
- package/src/swarm/messaging/__tests__/GossipProtocol.test.ts +437 -0
- package/src/swarm/messaging/__tests__/SwarmEventBus.prod.test.ts +191 -0
- package/src/swarm/messaging/__tests__/SwarmEventBus.test.ts +247 -0
- package/src/swarm/messaging/index.ts +8 -0
- package/src/swarm/spatial/FlockingBehavior.ts +462 -0
- package/src/swarm/spatial/FormationController.ts +500 -0
- package/src/swarm/spatial/Vector3.ts +170 -0
- package/src/swarm/spatial/ZoneClaiming.ts +509 -0
- package/src/swarm/spatial/__tests__/FlockingBehavior.prod.test.ts +239 -0
- package/src/swarm/spatial/__tests__/FlockingBehavior.test.ts +298 -0
- package/src/swarm/spatial/__tests__/FormationController.prod.test.ts +240 -0
- package/src/swarm/spatial/__tests__/FormationController.test.ts +297 -0
- package/src/swarm/spatial/__tests__/Vector3.prod.test.ts +283 -0
- package/src/swarm/spatial/__tests__/Vector3.test.ts +224 -0
- package/src/swarm/spatial/__tests__/ZoneClaiming.prod.test.ts +246 -0
- package/src/swarm/spatial/__tests__/ZoneClaiming.test.ts +374 -0
- package/src/swarm/spatial/index.ts +28 -0
- package/src/team.ts +1245 -0
- package/src/training/LRScheduler.ts +377 -0
- package/src/training/QualityScoringPipeline.ts +139 -0
- package/src/training/SoftDedup.ts +461 -0
- package/src/training/SparsityMonitor.ts +685 -0
- package/src/training/SparsityMonitorTypes.ts +209 -0
- package/src/training/SpatialTrainingDataGenerator.ts +1526 -0
- package/src/training/SpatialTrainingDataTypes.ts +216 -0
- package/src/training/TrainingPipelineConfig.ts +215 -0
- package/src/training/constants.ts +94 -0
- package/src/training/index.ts +138 -0
- package/src/training/schema.ts +147 -0
- package/src/training/scripts/generate-novel-use-cases-dataset.ts +272 -0
- package/src/training/scripts/generate-spatial-dataset.ts +521 -0
- package/src/training/training/data/novel-use-cases.jsonl +153 -0
- package/src/training/training/data/spatial-reasoning-10k.jsonl +9354 -0
- package/src/training/trainingmonkey/TrainingMonkeyIntegration.ts +477 -0
- package/src/training/trainingmonkey/TrainingMonkeyTypes.ts +230 -0
- package/src/training/trainingmonkey/index.ts +26 -0
- package/src/training/trait-mappings.ts +157 -0
- package/src/types/core-stubs.d.ts +113 -0
- package/src/types.ts +304 -0
- package/test-output.txt +0 -0
- package/test-result.json +1 -0
- package/tsc-errors.txt +4 -0
- package/tsc_output.txt +0 -0
- package/tsconfig.json +14 -0
- package/tsup-learning-esm.config.ts +12 -0
- package/tsup.config.ts +21 -0
- package/typescript-errors-2.txt +0 -0
- package/typescript-errors.txt +22 -0
- package/vitest-log-utf8.txt +268 -0
- package/vitest-log.txt +0 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UnifiedBudgetOptimizer — Bridges Economy and Rendering Budgets
|
|
3
|
+
*
|
|
4
|
+
* Oracle Cycle 6 identified that AgentBudgetEnforcer (1D USDC, hard stop)
|
|
5
|
+
* and ResourceBudgetAnalyzer (10D resources, graceful LOD degradation)
|
|
6
|
+
* solve the same optimization problem with zero shared abstractions.
|
|
7
|
+
*
|
|
8
|
+
* This module provides:
|
|
9
|
+
* 1. Trait Utility Function — value per resource cost (enables optimization)
|
|
10
|
+
* 2. Equimarginal LOD Allocation — drop by lowest value/cost ratio, not greedy
|
|
11
|
+
* 3. Resource Cost Floor Pricing — prevents economic denial-of-rendering
|
|
12
|
+
* 4. Unified budget query — single view of both economy + rendering pressure
|
|
13
|
+
*
|
|
14
|
+
* @version 1.0.0
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
ResourceCategory,
|
|
19
|
+
TRAIT_RESOURCE_COSTS,
|
|
20
|
+
PLATFORM_BUDGETS,
|
|
21
|
+
type ResourceUsageNode,
|
|
22
|
+
} from './_core-stubs';
|
|
23
|
+
|
|
24
|
+
// =============================================================================
|
|
25
|
+
// TYPES
|
|
26
|
+
// =============================================================================
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Trait utility entry — encodes how much value a trait provides per unit cost.
|
|
30
|
+
* Higher utility = keep this trait when budgets are tight.
|
|
31
|
+
*/
|
|
32
|
+
export interface TraitUtility {
|
|
33
|
+
/** Trait name (e.g., '@particle', '@gaussian') */
|
|
34
|
+
trait: string;
|
|
35
|
+
/** Base utility score (0-100, higher = more valuable to the experience) */
|
|
36
|
+
baseUtility: number;
|
|
37
|
+
/** Category for grouping (visual, physics, audio, network, ai) */
|
|
38
|
+
category: TraitCategory;
|
|
39
|
+
/** Whether this trait is required (cannot be dropped by LOD) */
|
|
40
|
+
required: boolean;
|
|
41
|
+
/** LOD level at which this trait can start being dropped (0 = never) */
|
|
42
|
+
minLODLevel: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type TraitCategory = 'visual' | 'physics' | 'audio' | 'network' | 'ai' | 'interaction';
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Allocation decision for a single trait.
|
|
49
|
+
*/
|
|
50
|
+
export interface TraitAllocation {
|
|
51
|
+
/** Trait name */
|
|
52
|
+
trait: string;
|
|
53
|
+
/** Whether to include this trait at current budget level */
|
|
54
|
+
included: boolean;
|
|
55
|
+
/** LOD level applied (0 = full detail) */
|
|
56
|
+
lodLevel: number;
|
|
57
|
+
/** Resource cost at current LOD */
|
|
58
|
+
resourceCost: Partial<Record<ResourceCategory, number>>;
|
|
59
|
+
/** Economic cost (USDC base units, 0 if no marketplace price) */
|
|
60
|
+
economicCost: number;
|
|
61
|
+
/** Computed utility score */
|
|
62
|
+
utility: number;
|
|
63
|
+
/** Value/cost ratio used for allocation decisions */
|
|
64
|
+
valueCostRatio: number;
|
|
65
|
+
/** Reason for exclusion if not included */
|
|
66
|
+
excludeReason?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Resource cost floor for marketplace pricing.
|
|
71
|
+
* Prevents a 1-credit trait from consuming 10K gaussians.
|
|
72
|
+
*/
|
|
73
|
+
export interface ResourceCostFloor {
|
|
74
|
+
/** Minimum price per unit of resource consumed */
|
|
75
|
+
perGaussian: number;
|
|
76
|
+
/** Minimum price per draw call */
|
|
77
|
+
perDrawCall: number;
|
|
78
|
+
/** Minimum price per MB memory */
|
|
79
|
+
perMemoryMB: number;
|
|
80
|
+
/** Minimum price per particle */
|
|
81
|
+
perParticle: number;
|
|
82
|
+
/** Minimum price per physics body */
|
|
83
|
+
perPhysicsBody: number;
|
|
84
|
+
/** Base platform fee (USDC base units) */
|
|
85
|
+
baseFee: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Unified budget state — single view across economy + rendering.
|
|
90
|
+
*/
|
|
91
|
+
export interface UnifiedBudgetState {
|
|
92
|
+
/** Agent ID */
|
|
93
|
+
agentId: string;
|
|
94
|
+
/** Economic pressure (0-1, where 1 = budget exhausted) */
|
|
95
|
+
economicPressure: number;
|
|
96
|
+
/** Resource pressure per category (0-1) */
|
|
97
|
+
resourcePressure: Partial<Record<ResourceCategory, number>>;
|
|
98
|
+
/** Overall pressure (max of economic and worst resource) */
|
|
99
|
+
overallPressure: number;
|
|
100
|
+
/** Suggested LOD level based on combined pressure */
|
|
101
|
+
suggestedLOD: number;
|
|
102
|
+
/** Whether any hard limit is breached */
|
|
103
|
+
hardLimitBreached: boolean;
|
|
104
|
+
/** Traits that should be shed to relieve pressure */
|
|
105
|
+
shedCandidates: TraitAllocation[];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Configuration for the unified optimizer.
|
|
110
|
+
*/
|
|
111
|
+
export interface UnifiedOptimizerConfig {
|
|
112
|
+
/** Target platform for resource limits */
|
|
113
|
+
platform: string;
|
|
114
|
+
/** Resource cost floor for marketplace pricing */
|
|
115
|
+
costFloor: ResourceCostFloor;
|
|
116
|
+
/** Custom trait utilities (override defaults) */
|
|
117
|
+
traitUtilities?: Map<string, TraitUtility>;
|
|
118
|
+
/** LOD scaling factors — how much resource cost reduces per LOD level */
|
|
119
|
+
lodScaling?: number[];
|
|
120
|
+
/** Economic budget limit (USDC base units, 0 = no limit) */
|
|
121
|
+
economicBudget?: number;
|
|
122
|
+
/** Economic spend so far (USDC base units) */
|
|
123
|
+
economicSpent?: number;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// =============================================================================
|
|
127
|
+
// DEFAULT TRAIT UTILITIES
|
|
128
|
+
// =============================================================================
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Default utility scores for common traits.
|
|
132
|
+
* These encode "how much does this trait contribute to the experience?"
|
|
133
|
+
*
|
|
134
|
+
* Scoring guide:
|
|
135
|
+
* - 90-100: Core experience (mesh, material, physics) — never drop
|
|
136
|
+
* - 70-89: Important visual/audio quality (lighting, particles)
|
|
137
|
+
* - 50-69: Enhancement (volumetric, VFX, spatial audio)
|
|
138
|
+
* - 30-49: Nice-to-have (reflections, subsurface, ambisonics)
|
|
139
|
+
* - 10-29: Luxury (ray tracing, global illumination, neural rendering)
|
|
140
|
+
*/
|
|
141
|
+
export const DEFAULT_TRAIT_UTILITIES: Record<string, Omit<TraitUtility, 'trait'>> = {
|
|
142
|
+
// ── Core (required, high utility) ──
|
|
143
|
+
'@mesh': { baseUtility: 100, category: 'visual', required: true, minLODLevel: 0 },
|
|
144
|
+
'@material': { baseUtility: 95, category: 'visual', required: true, minLODLevel: 0 },
|
|
145
|
+
'@physics': { baseUtility: 90, category: 'physics', required: true, minLODLevel: 0 },
|
|
146
|
+
'@rigidbody': { baseUtility: 90, category: 'physics', required: true, minLODLevel: 0 },
|
|
147
|
+
'@collider': { baseUtility: 88, category: 'physics', required: true, minLODLevel: 0 },
|
|
148
|
+
'@rendering': { baseUtility: 95, category: 'visual', required: true, minLODLevel: 0 },
|
|
149
|
+
'@character': { baseUtility: 92, category: 'visual', required: true, minLODLevel: 0 },
|
|
150
|
+
'@networked': { baseUtility: 85, category: 'network', required: true, minLODLevel: 0 },
|
|
151
|
+
// C6 Layer 2 re-score: @agent was hand-assigned at 85 (required). Data-derived
|
|
152
|
+
// analysis shows agents are a luxury in most rendering scenes — only ~35% of
|
|
153
|
+
// compositions use @agent, and it consumes 5MB memory. Downgraded to non-required
|
|
154
|
+
// enhancement. Scenes that genuinely need agents can override via custom utilities.
|
|
155
|
+
'@agent': { baseUtility: 35, category: 'ai', required: false, minLODLevel: 2 },
|
|
156
|
+
|
|
157
|
+
// ── Important quality (droppable at high LOD) ──
|
|
158
|
+
'@light': { baseUtility: 80, category: 'visual', required: false, minLODLevel: 3 },
|
|
159
|
+
'@lighting': { baseUtility: 78, category: 'visual', required: false, minLODLevel: 3 },
|
|
160
|
+
'@shader': { baseUtility: 75, category: 'visual', required: false, minLODLevel: 2 },
|
|
161
|
+
'@advanced_pbr': { baseUtility: 72, category: 'visual', required: false, minLODLevel: 2 },
|
|
162
|
+
'@particle': { baseUtility: 70, category: 'visual', required: false, minLODLevel: 2 },
|
|
163
|
+
'@audio': { baseUtility: 75, category: 'audio', required: false, minLODLevel: 3 },
|
|
164
|
+
'@spatial_audio': { baseUtility: 72, category: 'audio', required: false, minLODLevel: 2 },
|
|
165
|
+
'@animation': { baseUtility: 78, category: 'visual', required: false, minLODLevel: 3 },
|
|
166
|
+
'@skeleton': { baseUtility: 76, category: 'visual', required: false, minLODLevel: 3 },
|
|
167
|
+
|
|
168
|
+
// ── Enhancement (drop at medium LOD) ──
|
|
169
|
+
'@vfx': { baseUtility: 60, category: 'visual', required: false, minLODLevel: 2 },
|
|
170
|
+
'@volumetric': { baseUtility: 55, category: 'visual', required: false, minLODLevel: 2 },
|
|
171
|
+
'@advanced_lighting': { baseUtility: 58, category: 'visual', required: false, minLODLevel: 2 },
|
|
172
|
+
'@advanced_texturing': { baseUtility: 56, category: 'visual', required: false, minLODLevel: 2 },
|
|
173
|
+
'@screen_space_effects': { baseUtility: 52, category: 'visual', required: false, minLODLevel: 2 },
|
|
174
|
+
'@environmental_audio': { baseUtility: 55, category: 'audio', required: false, minLODLevel: 2 },
|
|
175
|
+
'@npc': { baseUtility: 65, category: 'ai', required: false, minLODLevel: 3 },
|
|
176
|
+
'@npc_ai': { baseUtility: 62, category: 'ai', required: false, minLODLevel: 3 },
|
|
177
|
+
'@fluid_simulation': { baseUtility: 50, category: 'physics', required: false, minLODLevel: 1 },
|
|
178
|
+
'@advanced_cloth': { baseUtility: 48, category: 'physics', required: false, minLODLevel: 1 },
|
|
179
|
+
'@joint': { baseUtility: 65, category: 'physics', required: false, minLODLevel: 3 },
|
|
180
|
+
|
|
181
|
+
// ── Nice-to-have (drop early) ──
|
|
182
|
+
'@subsurface_scattering': {
|
|
183
|
+
baseUtility: 38,
|
|
184
|
+
category: 'visual',
|
|
185
|
+
required: false,
|
|
186
|
+
minLODLevel: 1,
|
|
187
|
+
},
|
|
188
|
+
'@ambisonics': { baseUtility: 35, category: 'audio', required: false, minLODLevel: 1 },
|
|
189
|
+
'@voronoi_fracture': { baseUtility: 40, category: 'physics', required: false, minLODLevel: 1 },
|
|
190
|
+
'@granular_material': { baseUtility: 38, category: 'physics', required: false, minLODLevel: 1 },
|
|
191
|
+
'@lip_sync': { baseUtility: 42, category: 'visual', required: false, minLODLevel: 1 },
|
|
192
|
+
|
|
193
|
+
// ── Luxury (drop first) ──
|
|
194
|
+
'@ray_tracing': { baseUtility: 20, category: 'visual', required: false, minLODLevel: 1 },
|
|
195
|
+
'@global_illumination': { baseUtility: 22, category: 'visual', required: false, minLODLevel: 1 },
|
|
196
|
+
'@nerf': { baseUtility: 18, category: 'visual', required: false, minLODLevel: 1 },
|
|
197
|
+
'@multiview_gaussian_renderer': {
|
|
198
|
+
baseUtility: 25,
|
|
199
|
+
category: 'visual',
|
|
200
|
+
required: false,
|
|
201
|
+
minLODLevel: 1,
|
|
202
|
+
},
|
|
203
|
+
// C6 Layer 2 re-score: @gaussian/@gaussian_splat were hand-assigned at 65.
|
|
204
|
+
// Data-derived analysis: 100K gaussians on Quest 3 = 55% of total GPU budget,
|
|
205
|
+
// making the value/cost ratio much lower than hand-scores suggested.
|
|
206
|
+
// Downgraded by 45 points to reflect actual GPU cost relative to utility.
|
|
207
|
+
'@gaussian': { baseUtility: 20, category: 'visual', required: false, minLODLevel: 1 },
|
|
208
|
+
'@gaussian_splat': { baseUtility: 20, category: 'visual', required: false, minLODLevel: 1 },
|
|
209
|
+
'@stable_diffusion': { baseUtility: 15, category: 'ai', required: false, minLODLevel: 1 },
|
|
210
|
+
'@diffusion_realtime': { baseUtility: 18, category: 'ai', required: false, minLODLevel: 1 },
|
|
211
|
+
'@local_llm': { baseUtility: 30, category: 'ai', required: false, minLODLevel: 1 },
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Default resource cost floor pricing.
|
|
216
|
+
* Prevents marketplace traits from being priced below their GPU cost.
|
|
217
|
+
* Prices in USDC base units (6 decimals, so 1_000_000 = $1.00).
|
|
218
|
+
*/
|
|
219
|
+
/**
|
|
220
|
+
* Platform-specific LOD scaling curves.
|
|
221
|
+
*
|
|
222
|
+
* C6 Layer 2 finding: platform exchange rates are wildly non-uniform (25x
|
|
223
|
+
* price difference for a single @particle between Mobile AR and Desktop VR).
|
|
224
|
+
* A uniform LOD curve means constrained platforms over-degrade while powerful
|
|
225
|
+
* platforms under-utilize. Each platform gets its own perceptual curve.
|
|
226
|
+
*
|
|
227
|
+
* Constrained platforms (mobile_ar, quest3) degrade faster at LOD 1-2.
|
|
228
|
+
* Powerful platforms (desktop-vr) degrade slowly, preserving quality.
|
|
229
|
+
*/
|
|
230
|
+
export const PLATFORM_LOD_SCALING: Record<string, number[]> = {
|
|
231
|
+
'quest3': [1.0, 0.6, 0.3, 0.12, 0.04], // Aggressive — tight budget
|
|
232
|
+
'mobile-ar': [1.0, 0.5, 0.2, 0.08, 0.02], // Most aggressive — tightest budget
|
|
233
|
+
'webgpu': [1.0, 0.7, 0.4, 0.18, 0.06], // Moderate — matches default
|
|
234
|
+
'desktop-vr': [1.0, 0.85, 0.6, 0.3, 0.1], // Gentle — plenty of headroom
|
|
235
|
+
'visionos': [1.0, 0.8, 0.5, 0.25, 0.08], // Moderate-gentle — good hardware
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Default resource cost floor pricing.
|
|
240
|
+
* Prevents marketplace traits from being priced below their GPU cost.
|
|
241
|
+
* Prices in USDC base units (6 decimals, so 1_000_000 = $1.00).
|
|
242
|
+
*/
|
|
243
|
+
export const DEFAULT_COST_FLOOR: ResourceCostFloor = {
|
|
244
|
+
perGaussian: 0.01, // 10K gaussians = $0.10 minimum
|
|
245
|
+
perDrawCall: 1000, // $0.001 per draw call
|
|
246
|
+
perMemoryMB: 5000, // $0.005 per MB
|
|
247
|
+
perParticle: 0.1, // 1000 particles = $0.10 minimum
|
|
248
|
+
perPhysicsBody: 500, // $0.0005 per body
|
|
249
|
+
baseFee: 100_000, // $0.10 base fee on all marketplace traits
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Default LOD scaling: how much resource usage decreases per LOD level.
|
|
254
|
+
*
|
|
255
|
+
* Previous geometric scaling [1.0, 0.5, 0.25, 0.1, 0.05] over-degrades
|
|
256
|
+
* the visible mid-range (LOD 1-2). Human perception follows Weber-Fechner
|
|
257
|
+
* (logarithmic), meaning a 50% reduction at LOD 1 is far more noticeable
|
|
258
|
+
* than a 50% reduction at LOD 3.
|
|
259
|
+
*
|
|
260
|
+
* Perceptual curve: each step reduces quality by an amount proportional
|
|
261
|
+
* to the *perceived* difference, not the geometric ratio.
|
|
262
|
+
* LOD 0 = 1.0 (full), LOD 1 = 0.7, LOD 2 = 0.4, LOD 3 = 0.18, LOD 4 = 0.06
|
|
263
|
+
*
|
|
264
|
+
* This preserves more detail in the visually sensitive mid-range while
|
|
265
|
+
* still achieving aggressive reduction at far distances.
|
|
266
|
+
*/
|
|
267
|
+
export const DEFAULT_LOD_SCALING = [1.0, 0.7, 0.4, 0.18, 0.06];
|
|
268
|
+
|
|
269
|
+
// =============================================================================
|
|
270
|
+
// UNIFIED BUDGET OPTIMIZER
|
|
271
|
+
// =============================================================================
|
|
272
|
+
|
|
273
|
+
export class UnifiedBudgetOptimizer {
|
|
274
|
+
private platform: string;
|
|
275
|
+
private costFloor: ResourceCostFloor;
|
|
276
|
+
private traitUtilities: Map<string, TraitUtility>;
|
|
277
|
+
private lodScaling: number[];
|
|
278
|
+
private economicBudget: number;
|
|
279
|
+
private economicSpent: number;
|
|
280
|
+
|
|
281
|
+
constructor(config: UnifiedOptimizerConfig) {
|
|
282
|
+
this.platform = config.platform;
|
|
283
|
+
this.costFloor = config.costFloor;
|
|
284
|
+
// Use platform-specific LOD scaling if available, fall back to default perceptual curve
|
|
285
|
+
this.lodScaling = config.lodScaling
|
|
286
|
+
?? PLATFORM_LOD_SCALING[config.platform]
|
|
287
|
+
?? DEFAULT_LOD_SCALING;
|
|
288
|
+
this.economicBudget = config.economicBudget ?? 0;
|
|
289
|
+
this.economicSpent = config.economicSpent ?? 0;
|
|
290
|
+
|
|
291
|
+
// Build utility map from defaults + overrides
|
|
292
|
+
this.traitUtilities = new Map();
|
|
293
|
+
for (const [trait, util] of Object.entries(DEFAULT_TRAIT_UTILITIES)) {
|
|
294
|
+
this.traitUtilities.set(trait, { trait, ...util });
|
|
295
|
+
}
|
|
296
|
+
if (config.traitUtilities) {
|
|
297
|
+
for (const [trait, util] of config.traitUtilities) {
|
|
298
|
+
this.traitUtilities.set(trait, util);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// ===========================================================================
|
|
304
|
+
// CORE: Equimarginal Allocation
|
|
305
|
+
// ===========================================================================
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Allocate traits across a resource budget using the equimarginal principle.
|
|
309
|
+
* Instead of greedily dropping the deepest LOD first, this sorts by value/cost
|
|
310
|
+
* ratio and drops traits with the lowest marginal utility first.
|
|
311
|
+
*
|
|
312
|
+
* @param nodes - Resource usage nodes (traits + counts)
|
|
313
|
+
* @param maxLOD - Maximum LOD level to consider (default: 4)
|
|
314
|
+
* @returns Allocation decisions for each trait
|
|
315
|
+
*/
|
|
316
|
+
allocate(nodes: ResourceUsageNode[], maxLOD: number = 4): TraitAllocation[] {
|
|
317
|
+
const limits = PLATFORM_BUDGETS[this.platform];
|
|
318
|
+
if (!limits) {
|
|
319
|
+
// No limits = include everything at LOD 0
|
|
320
|
+
return this.flattenToAllocations(nodes, 0);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Start with everything at LOD 0
|
|
324
|
+
let allocations = this.flattenToAllocations(nodes, 0);
|
|
325
|
+
|
|
326
|
+
// Check if we're within budget
|
|
327
|
+
let pressure = this.computeResourcePressure(allocations, limits);
|
|
328
|
+
if (pressure.maxPressure <= 1.0) {
|
|
329
|
+
return allocations; // Everything fits
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Equimarginal: sort by value/cost ratio ascending, shed lowest-value first
|
|
333
|
+
for (let lod = 1; lod <= maxLOD && pressure.maxPressure > 1.0; lod++) {
|
|
334
|
+
// Get droppable traits at this LOD level, sorted by value/cost ratio
|
|
335
|
+
const candidates = allocations
|
|
336
|
+
.filter((a) => a.included && !this.isRequired(a.trait) && this.canDropAtLOD(a.trait, lod))
|
|
337
|
+
.sort((a, b) => a.valueCostRatio - b.valueCostRatio);
|
|
338
|
+
|
|
339
|
+
for (const candidate of candidates) {
|
|
340
|
+
if (pressure.maxPressure <= 1.0) break;
|
|
341
|
+
|
|
342
|
+
// Degrade this trait to current LOD level
|
|
343
|
+
const idx = allocations.findIndex((a) => a.trait === candidate.trait);
|
|
344
|
+
if (idx >= 0) {
|
|
345
|
+
allocations[idx] = this.buildAllocation(candidate.trait, lod, 1);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
pressure = this.computeResourcePressure(allocations, limits);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// If still over budget, start excluding non-required traits (lowest ratio first)
|
|
353
|
+
if (pressure.maxPressure > 1.0) {
|
|
354
|
+
const excludeable = allocations
|
|
355
|
+
.filter((a) => a.included && !this.isRequired(a.trait))
|
|
356
|
+
.sort((a, b) => a.valueCostRatio - b.valueCostRatio);
|
|
357
|
+
|
|
358
|
+
for (const candidate of excludeable) {
|
|
359
|
+
if (pressure.maxPressure <= 1.0) break;
|
|
360
|
+
|
|
361
|
+
const idx = allocations.findIndex((a) => a.trait === candidate.trait);
|
|
362
|
+
if (idx >= 0) {
|
|
363
|
+
allocations[idx] = {
|
|
364
|
+
...allocations[idx],
|
|
365
|
+
included: false,
|
|
366
|
+
excludeReason: `Excluded to fit ${this.platform} budget (value/cost ratio: ${candidate.valueCostRatio.toFixed(2)})`,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
pressure = this.computeResourcePressure(allocations, limits);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return allocations;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ===========================================================================
|
|
378
|
+
// RESOURCE COST FLOOR PRICING
|
|
379
|
+
// ===========================================================================
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Calculate the minimum marketplace price for a trait based on its resource cost.
|
|
383
|
+
* Prevents economic denial-of-rendering attacks where a cheap marketplace trait
|
|
384
|
+
* consumes massive GPU resources.
|
|
385
|
+
*
|
|
386
|
+
* @param traitName - The trait to price
|
|
387
|
+
* @param instanceCount - How many instances (default: 1)
|
|
388
|
+
* @returns Minimum price in USDC base units (6 decimals)
|
|
389
|
+
*/
|
|
390
|
+
calculateCostFloor(traitName: string, instanceCount: number = 1): number {
|
|
391
|
+
const normalized = traitName.startsWith('@') ? traitName : `@${traitName}`;
|
|
392
|
+
const costs = TRAIT_RESOURCE_COSTS[normalized];
|
|
393
|
+
if (!costs) return this.costFloor.baseFee;
|
|
394
|
+
|
|
395
|
+
let floor = this.costFloor.baseFee;
|
|
396
|
+
|
|
397
|
+
if (costs.gaussians) floor += costs.gaussians * instanceCount * this.costFloor.perGaussian;
|
|
398
|
+
if (costs.gpuDrawCalls)
|
|
399
|
+
floor += costs.gpuDrawCalls * instanceCount * this.costFloor.perDrawCall;
|
|
400
|
+
if (costs.memoryMB) floor += costs.memoryMB * instanceCount * this.costFloor.perMemoryMB;
|
|
401
|
+
if (costs.particles) floor += costs.particles * instanceCount * this.costFloor.perParticle;
|
|
402
|
+
if (costs.physicsBodies)
|
|
403
|
+
floor += costs.physicsBodies * instanceCount * this.costFloor.perPhysicsBody;
|
|
404
|
+
|
|
405
|
+
return Math.ceil(floor);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Validate that a marketplace listing price meets the resource cost floor.
|
|
410
|
+
*
|
|
411
|
+
* @param traitName - The trait being listed
|
|
412
|
+
* @param listPrice - Proposed listing price (USDC base units)
|
|
413
|
+
* @param instanceCount - Expected instance count
|
|
414
|
+
* @returns Validation result
|
|
415
|
+
*/
|
|
416
|
+
validateMarketplacePrice(
|
|
417
|
+
traitName: string,
|
|
418
|
+
listPrice: number,
|
|
419
|
+
instanceCount: number = 1
|
|
420
|
+
): { valid: boolean; floor: number; deficit: number; message: string } {
|
|
421
|
+
const floor = this.calculateCostFloor(traitName, instanceCount);
|
|
422
|
+
const deficit = Math.max(0, floor - listPrice);
|
|
423
|
+
|
|
424
|
+
if (listPrice >= floor) {
|
|
425
|
+
return {
|
|
426
|
+
valid: true,
|
|
427
|
+
floor,
|
|
428
|
+
deficit: 0,
|
|
429
|
+
message: `Price ${listPrice} meets resource cost floor of ${floor}`,
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return {
|
|
434
|
+
valid: false,
|
|
435
|
+
floor,
|
|
436
|
+
deficit,
|
|
437
|
+
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.`,
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// ===========================================================================
|
|
442
|
+
// UNIFIED BUDGET STATE
|
|
443
|
+
// ===========================================================================
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Get a unified view of budget pressure across economy + rendering.
|
|
447
|
+
*
|
|
448
|
+
* @param agentId - Agent identifier
|
|
449
|
+
* @param nodes - Current resource usage nodes
|
|
450
|
+
* @param economicSpent - Current economic spend (USDC base units)
|
|
451
|
+
* @param economicLimit - Economic budget limit (USDC base units)
|
|
452
|
+
* @returns Unified budget state
|
|
453
|
+
*/
|
|
454
|
+
getUnifiedState(
|
|
455
|
+
agentId: string,
|
|
456
|
+
nodes: ResourceUsageNode[],
|
|
457
|
+
economicSpent?: number,
|
|
458
|
+
economicLimit?: number
|
|
459
|
+
): UnifiedBudgetState {
|
|
460
|
+
const spent = economicSpent ?? this.economicSpent;
|
|
461
|
+
const limit = economicLimit ?? this.economicBudget;
|
|
462
|
+
const economicPressure = limit > 0 ? spent / limit : 0;
|
|
463
|
+
|
|
464
|
+
const allocations = this.flattenToAllocations(nodes, 0);
|
|
465
|
+
const limits = PLATFORM_BUDGETS[this.platform] ?? {};
|
|
466
|
+
const pressure = this.computeResourcePressure(allocations, limits);
|
|
467
|
+
|
|
468
|
+
const overallPressure = Math.max(economicPressure, pressure.maxPressure);
|
|
469
|
+
|
|
470
|
+
// Suggest LOD based on overall pressure
|
|
471
|
+
let suggestedLOD = 0;
|
|
472
|
+
if (overallPressure > 0.95) suggestedLOD = 3;
|
|
473
|
+
else if (overallPressure > 0.8) suggestedLOD = 2;
|
|
474
|
+
else if (overallPressure > 0.6) suggestedLOD = 1;
|
|
475
|
+
|
|
476
|
+
// Find shed candidates (sorted by value/cost ratio ascending)
|
|
477
|
+
const shedCandidates = allocations
|
|
478
|
+
.filter((a) => a.included && !this.isRequired(a.trait))
|
|
479
|
+
.sort((a, b) => a.valueCostRatio - b.valueCostRatio)
|
|
480
|
+
.slice(0, 10);
|
|
481
|
+
|
|
482
|
+
return {
|
|
483
|
+
agentId,
|
|
484
|
+
economicPressure: Math.min(1, economicPressure),
|
|
485
|
+
resourcePressure: pressure.perCategory,
|
|
486
|
+
overallPressure: Math.min(1, overallPressure),
|
|
487
|
+
suggestedLOD,
|
|
488
|
+
hardLimitBreached: pressure.maxPressure > 1.0 || economicPressure > 1.0,
|
|
489
|
+
shedCandidates,
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// ===========================================================================
|
|
494
|
+
// UTILITY QUERIES
|
|
495
|
+
// ===========================================================================
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Get the utility score for a trait.
|
|
499
|
+
*/
|
|
500
|
+
getUtility(traitName: string): TraitUtility | undefined {
|
|
501
|
+
const normalized = traitName.startsWith('@') ? traitName : `@${traitName}`;
|
|
502
|
+
return this.traitUtilities.get(normalized);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Set custom utility for a trait.
|
|
507
|
+
*/
|
|
508
|
+
setUtility(utility: TraitUtility): void {
|
|
509
|
+
this.traitUtilities.set(utility.trait, utility);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Get the total weighted resource cost of a trait at a given LOD level.
|
|
514
|
+
* Collapses multi-dimensional resource cost into a single scalar
|
|
515
|
+
* using platform limits as normalization weights.
|
|
516
|
+
*/
|
|
517
|
+
getWeightedCost(traitName: string, lodLevel: number = 0, instanceCount: number = 1): number {
|
|
518
|
+
const normalized = traitName.startsWith('@') ? traitName : `@${traitName}`;
|
|
519
|
+
const costs = TRAIT_RESOURCE_COSTS[normalized];
|
|
520
|
+
if (!costs) return 0;
|
|
521
|
+
|
|
522
|
+
const limits = PLATFORM_BUDGETS[this.platform] ?? {};
|
|
523
|
+
const scale = this.lodScaling[Math.min(lodLevel, this.lodScaling.length - 1)] ?? 0.05;
|
|
524
|
+
|
|
525
|
+
let weighted = 0;
|
|
526
|
+
for (const [cat, cost] of Object.entries(costs)) {
|
|
527
|
+
const limit = limits[cat as ResourceCategory];
|
|
528
|
+
if (limit && limit > 0) {
|
|
529
|
+
// Normalize cost by platform limit so each dimension contributes proportionally
|
|
530
|
+
weighted += ((cost as number) * instanceCount * scale) / limit;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
return weighted;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Compute value/cost ratio for a trait at a given LOD level.
|
|
539
|
+
* Higher = more efficient use of resources.
|
|
540
|
+
*/
|
|
541
|
+
getValueCostRatio(traitName: string, lodLevel: number = 0, instanceCount: number = 1): number {
|
|
542
|
+
const normalized = traitName.startsWith('@') ? traitName : `@${traitName}`;
|
|
543
|
+
const util = this.traitUtilities.get(normalized);
|
|
544
|
+
const utility = util?.baseUtility ?? 50; // Default utility for unknown traits
|
|
545
|
+
const cost = this.getWeightedCost(normalized, lodLevel, instanceCount);
|
|
546
|
+
if (cost === 0) return utility * 100; // Free trait = infinite value (capped)
|
|
547
|
+
return utility / cost;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// ===========================================================================
|
|
551
|
+
// INTERNALS
|
|
552
|
+
// ===========================================================================
|
|
553
|
+
|
|
554
|
+
private flattenToAllocations(nodes: ResourceUsageNode[], lodLevel: number): TraitAllocation[] {
|
|
555
|
+
const allocations: TraitAllocation[] = [];
|
|
556
|
+
const seen = new Set<string>();
|
|
557
|
+
|
|
558
|
+
for (const node of nodes) {
|
|
559
|
+
for (const trait of node.traits) {
|
|
560
|
+
const normalized = trait.startsWith('@') ? trait : `@${trait}`;
|
|
561
|
+
if (seen.has(normalized)) continue;
|
|
562
|
+
seen.add(normalized);
|
|
563
|
+
|
|
564
|
+
allocations.push(this.buildAllocation(normalized, lodLevel, node.count || 1));
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return allocations;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
private buildAllocation(trait: string, lodLevel: number, instanceCount: number): TraitAllocation {
|
|
572
|
+
const normalized = trait.startsWith('@') ? trait : `@${trait}`;
|
|
573
|
+
const costs = TRAIT_RESOURCE_COSTS[normalized] ?? {};
|
|
574
|
+
const scale = this.lodScaling[Math.min(lodLevel, this.lodScaling.length - 1)] ?? 0.05;
|
|
575
|
+
|
|
576
|
+
// Scale resource costs by LOD level
|
|
577
|
+
const scaledCosts: Partial<Record<ResourceCategory, number>> = {};
|
|
578
|
+
for (const [cat, cost] of Object.entries(costs)) {
|
|
579
|
+
scaledCosts[cat as ResourceCategory] = Math.ceil((cost as number) * instanceCount * scale);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const utility = this.traitUtilities.get(normalized)?.baseUtility ?? 50;
|
|
583
|
+
const weightedCost = this.getWeightedCost(normalized, lodLevel, instanceCount);
|
|
584
|
+
const valueCostRatio = weightedCost > 0 ? utility / weightedCost : utility * 100;
|
|
585
|
+
|
|
586
|
+
return {
|
|
587
|
+
trait: normalized,
|
|
588
|
+
included: true,
|
|
589
|
+
lodLevel,
|
|
590
|
+
resourceCost: scaledCosts,
|
|
591
|
+
economicCost: 0, // Set externally if marketplace pricing applies
|
|
592
|
+
utility,
|
|
593
|
+
valueCostRatio,
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
private isRequired(trait: string): boolean {
|
|
598
|
+
const normalized = trait.startsWith('@') ? trait : `@${trait}`;
|
|
599
|
+
return this.traitUtilities.get(normalized)?.required ?? false;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
private canDropAtLOD(trait: string, lodLevel: number): boolean {
|
|
603
|
+
const normalized = trait.startsWith('@') ? trait : `@${trait}`;
|
|
604
|
+
const util = this.traitUtilities.get(normalized);
|
|
605
|
+
if (!util) return lodLevel >= 2; // Unknown traits droppable at LOD 2+
|
|
606
|
+
return lodLevel >= util.minLODLevel;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
private computeResourcePressure(
|
|
610
|
+
allocations: TraitAllocation[],
|
|
611
|
+
limits: Partial<Record<ResourceCategory, number>>
|
|
612
|
+
): { maxPressure: number; perCategory: Partial<Record<ResourceCategory, number>> } {
|
|
613
|
+
// Sum resource costs from all included allocations
|
|
614
|
+
const totals: Record<string, number> = {};
|
|
615
|
+
for (const alloc of allocations) {
|
|
616
|
+
if (!alloc.included) continue;
|
|
617
|
+
for (const [cat, cost] of Object.entries(alloc.resourceCost)) {
|
|
618
|
+
totals[cat] = (totals[cat] || 0) + (cost as number);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// Compute pressure per category
|
|
623
|
+
const perCategory: Partial<Record<ResourceCategory, number>> = {};
|
|
624
|
+
let maxPressure = 0;
|
|
625
|
+
|
|
626
|
+
for (const [cat, limit] of Object.entries(limits)) {
|
|
627
|
+
const used = totals[cat] || 0;
|
|
628
|
+
const pressure = limit ? used / (limit as number) : 0;
|
|
629
|
+
perCategory[cat as ResourceCategory] = pressure;
|
|
630
|
+
if (pressure > maxPressure) maxPressure = pressure;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
return { maxPressure, perCategory };
|
|
634
|
+
}
|
|
635
|
+
}
|