@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,621 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @holoscript/core - Layer 1: Real-Time Communication Layer
|
|
3
|
+
*
|
|
4
|
+
* UDP/WebRTC-based real-time communication for 90fps agent coordination.
|
|
5
|
+
* Features:
|
|
6
|
+
* - <1ms latency for position sync and frame budget updates
|
|
7
|
+
* - Binary protocol for minimal overhead
|
|
8
|
+
* - 90 messages/second per agent capability
|
|
9
|
+
* - Spatial conflict detection and alerts
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { EventEmitter } from 'events';
|
|
13
|
+
import { DEFAULT_REALTIME_CONFIG } from './ProtocolTypes';
|
|
14
|
+
import type {
|
|
15
|
+
RealTimeMessage,
|
|
16
|
+
RealTimeMessageBody,
|
|
17
|
+
RealTimeProtocolConfig,
|
|
18
|
+
PositionSyncMessage,
|
|
19
|
+
FrameBudgetMessage,
|
|
20
|
+
} from './ProtocolTypes';
|
|
21
|
+
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// MESSAGE ENCODING/DECODING (Binary Protocol)
|
|
24
|
+
// ============================================================================
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Binary message header structure (12 bytes)
|
|
28
|
+
* - Message type (1 byte)
|
|
29
|
+
* - Agent ID length (1 byte)
|
|
30
|
+
* - Timestamp (8 bytes, microseconds)
|
|
31
|
+
* - Reserved (2 bytes)
|
|
32
|
+
*/
|
|
33
|
+
const HEADER_SIZE = 12;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Message type enum for binary encoding
|
|
37
|
+
*/
|
|
38
|
+
enum MessageTypeCode {
|
|
39
|
+
POSITION_SYNC = 0x01,
|
|
40
|
+
FRAME_BUDGET = 0x02,
|
|
41
|
+
SPATIAL_CONFLICT = 0x03,
|
|
42
|
+
PERFORMANCE_METRIC = 0x04,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Encode real-time message to binary format
|
|
47
|
+
*/
|
|
48
|
+
export function encodeRealTimeMessage(message: RealTimeMessage): Buffer {
|
|
49
|
+
// For binary encoding, we use a compact format
|
|
50
|
+
// Header: type (1) + agent_id_len (1) + timestamp (8) + reserved (2) = 12 bytes
|
|
51
|
+
// Body: depends on message type
|
|
52
|
+
|
|
53
|
+
const agentIdBytes = Buffer.from(message.agent_id, 'utf-8');
|
|
54
|
+
const agentIdLen = agentIdBytes.length;
|
|
55
|
+
|
|
56
|
+
// Determine message type code
|
|
57
|
+
let typeCode: number;
|
|
58
|
+
switch (message.type) {
|
|
59
|
+
case 'position_sync':
|
|
60
|
+
typeCode = MessageTypeCode.POSITION_SYNC;
|
|
61
|
+
break;
|
|
62
|
+
case 'frame_budget':
|
|
63
|
+
typeCode = MessageTypeCode.FRAME_BUDGET;
|
|
64
|
+
break;
|
|
65
|
+
case 'spatial_conflict':
|
|
66
|
+
typeCode = MessageTypeCode.SPATIAL_CONFLICT;
|
|
67
|
+
break;
|
|
68
|
+
case 'performance_metric':
|
|
69
|
+
typeCode = MessageTypeCode.PERFORMANCE_METRIC;
|
|
70
|
+
break;
|
|
71
|
+
default:
|
|
72
|
+
throw new Error(`Unknown message type: ${(message as unknown as Record<string, unknown>).type}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Encode based on type
|
|
76
|
+
if (message.type === 'position_sync') {
|
|
77
|
+
const msg = message as PositionSyncMessage;
|
|
78
|
+
// Header (12) + agent_id_len + position (12) + rotation (16) + scale (12) = 52 + agent_id_len
|
|
79
|
+
const bodySize = agentIdLen + 12 + 16 + 12 + (msg.velocity ? 12 : 0);
|
|
80
|
+
const buffer = Buffer.allocUnsafe(HEADER_SIZE + bodySize);
|
|
81
|
+
let offset = 0;
|
|
82
|
+
|
|
83
|
+
// Header
|
|
84
|
+
buffer.writeUInt8(typeCode, offset);
|
|
85
|
+
offset += 1;
|
|
86
|
+
buffer.writeUInt8(agentIdLen, offset);
|
|
87
|
+
offset += 1;
|
|
88
|
+
buffer.writeBigInt64BE(BigInt(msg.timestamp), offset);
|
|
89
|
+
offset += 8;
|
|
90
|
+
buffer.writeUInt16BE(0, offset);
|
|
91
|
+
offset += 2; // Reserved
|
|
92
|
+
|
|
93
|
+
// Agent ID
|
|
94
|
+
agentIdBytes.copy(buffer, offset);
|
|
95
|
+
offset += agentIdLen;
|
|
96
|
+
|
|
97
|
+
// Position (3 floats = 12 bytes)
|
|
98
|
+
buffer.writeFloatBE(msg.position[0], offset);
|
|
99
|
+
offset += 4;
|
|
100
|
+
buffer.writeFloatBE(msg.position[1], offset);
|
|
101
|
+
offset += 4;
|
|
102
|
+
buffer.writeFloatBE(msg.position[2], offset);
|
|
103
|
+
offset += 4;
|
|
104
|
+
|
|
105
|
+
// Rotation (4 floats = 16 bytes)
|
|
106
|
+
buffer.writeFloatBE(msg.rotation[0], offset);
|
|
107
|
+
offset += 4;
|
|
108
|
+
buffer.writeFloatBE(msg.rotation[1], offset);
|
|
109
|
+
offset += 4;
|
|
110
|
+
buffer.writeFloatBE(msg.rotation[2], offset);
|
|
111
|
+
offset += 4;
|
|
112
|
+
buffer.writeFloatBE(msg.rotation[3], offset);
|
|
113
|
+
offset += 4;
|
|
114
|
+
|
|
115
|
+
// Scale (3 floats = 12 bytes)
|
|
116
|
+
buffer.writeFloatBE(msg.scale[0], offset);
|
|
117
|
+
offset += 4;
|
|
118
|
+
buffer.writeFloatBE(msg.scale[1], offset);
|
|
119
|
+
offset += 4;
|
|
120
|
+
buffer.writeFloatBE(msg.scale[2], offset);
|
|
121
|
+
offset += 4;
|
|
122
|
+
|
|
123
|
+
// Velocity (optional, 3 floats = 12 bytes)
|
|
124
|
+
if (msg.velocity) {
|
|
125
|
+
buffer.writeFloatBE(msg.velocity[0], offset);
|
|
126
|
+
offset += 4;
|
|
127
|
+
buffer.writeFloatBE(msg.velocity[1], offset);
|
|
128
|
+
offset += 4;
|
|
129
|
+
buffer.writeFloatBE(msg.velocity[2], offset);
|
|
130
|
+
offset += 4;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return buffer;
|
|
134
|
+
} else if (message.type === 'frame_budget') {
|
|
135
|
+
const msg = message as FrameBudgetMessage;
|
|
136
|
+
// Header (12) + agent_id_len + frame_time (4) + budget_remaining (4) + target_fps (4) + actual_fps (4) + quality (1) = 29 + agent_id_len
|
|
137
|
+
const bodySize = agentIdLen + 17;
|
|
138
|
+
const buffer = Buffer.allocUnsafe(HEADER_SIZE + bodySize);
|
|
139
|
+
let offset = 0;
|
|
140
|
+
|
|
141
|
+
// Header
|
|
142
|
+
buffer.writeUInt8(typeCode, offset);
|
|
143
|
+
offset += 1;
|
|
144
|
+
buffer.writeUInt8(agentIdLen, offset);
|
|
145
|
+
offset += 1;
|
|
146
|
+
buffer.writeBigInt64BE(BigInt(msg.timestamp), offset);
|
|
147
|
+
offset += 8;
|
|
148
|
+
buffer.writeUInt16BE(0, offset);
|
|
149
|
+
offset += 2;
|
|
150
|
+
|
|
151
|
+
// Agent ID
|
|
152
|
+
agentIdBytes.copy(buffer, offset);
|
|
153
|
+
offset += agentIdLen;
|
|
154
|
+
|
|
155
|
+
// Frame budget data
|
|
156
|
+
buffer.writeFloatBE(msg.frame_time_ms, offset);
|
|
157
|
+
offset += 4;
|
|
158
|
+
buffer.writeFloatBE(msg.budget_remaining_ms, offset);
|
|
159
|
+
offset += 4;
|
|
160
|
+
buffer.writeFloatBE(msg.target_fps, offset);
|
|
161
|
+
offset += 4;
|
|
162
|
+
buffer.writeFloatBE(msg.actual_fps, offset);
|
|
163
|
+
offset += 4;
|
|
164
|
+
|
|
165
|
+
// Quality level (1 byte)
|
|
166
|
+
const qualityCode = { high: 0, medium: 1, low: 2, minimal: 3 }[msg.quality_level];
|
|
167
|
+
buffer.writeUInt8(qualityCode, offset);
|
|
168
|
+
offset += 1;
|
|
169
|
+
|
|
170
|
+
return buffer;
|
|
171
|
+
} else {
|
|
172
|
+
// For other message types, fall back to JSON encoding
|
|
173
|
+
// (These are less frequent and can afford the overhead)
|
|
174
|
+
const json = JSON.stringify(message);
|
|
175
|
+
const jsonBytes = Buffer.from(json, 'utf-8');
|
|
176
|
+
const buffer = Buffer.allocUnsafe(HEADER_SIZE + agentIdLen + jsonBytes.length);
|
|
177
|
+
let offset = 0;
|
|
178
|
+
|
|
179
|
+
// Header
|
|
180
|
+
buffer.writeUInt8(typeCode, offset);
|
|
181
|
+
offset += 1;
|
|
182
|
+
buffer.writeUInt8(agentIdLen, offset);
|
|
183
|
+
offset += 1;
|
|
184
|
+
buffer.writeBigInt64BE(BigInt(message.timestamp), offset);
|
|
185
|
+
offset += 8;
|
|
186
|
+
buffer.writeUInt16BE(0, offset);
|
|
187
|
+
offset += 2;
|
|
188
|
+
|
|
189
|
+
// Agent ID
|
|
190
|
+
agentIdBytes.copy(buffer, offset);
|
|
191
|
+
offset += agentIdLen;
|
|
192
|
+
|
|
193
|
+
// JSON payload
|
|
194
|
+
jsonBytes.copy(buffer, offset);
|
|
195
|
+
|
|
196
|
+
return buffer;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Decode binary message to RealTimeMessage
|
|
202
|
+
*/
|
|
203
|
+
export function decodeRealTimeMessage(buffer: Buffer): RealTimeMessage {
|
|
204
|
+
let offset = 0;
|
|
205
|
+
|
|
206
|
+
// Read header
|
|
207
|
+
const typeCode = buffer.readUInt8(offset);
|
|
208
|
+
offset += 1;
|
|
209
|
+
const agentIdLen = buffer.readUInt8(offset);
|
|
210
|
+
offset += 1;
|
|
211
|
+
const timestamp = Number(buffer.readBigInt64BE(offset));
|
|
212
|
+
offset += 8;
|
|
213
|
+
offset += 2; // Skip reserved
|
|
214
|
+
|
|
215
|
+
// Read agent ID
|
|
216
|
+
const agentId = buffer.toString('utf-8', offset, offset + agentIdLen);
|
|
217
|
+
offset += agentIdLen;
|
|
218
|
+
|
|
219
|
+
// Decode based on type
|
|
220
|
+
if (typeCode === MessageTypeCode.POSITION_SYNC) {
|
|
221
|
+
const px = buffer.readFloatBE(offset); offset += 4;
|
|
222
|
+
const py = buffer.readFloatBE(offset); offset += 4;
|
|
223
|
+
const pz = buffer.readFloatBE(offset); offset += 4;
|
|
224
|
+
const position: [number, number, number] = [px, py, pz];
|
|
225
|
+
|
|
226
|
+
const rx = buffer.readFloatBE(offset); offset += 4;
|
|
227
|
+
const ry = buffer.readFloatBE(offset); offset += 4;
|
|
228
|
+
const rz = buffer.readFloatBE(offset); offset += 4;
|
|
229
|
+
const rw = buffer.readFloatBE(offset); offset += 4;
|
|
230
|
+
const rotation: [number, number, number, number] = [rx, ry, rz, rw];
|
|
231
|
+
|
|
232
|
+
const sx = buffer.readFloatBE(offset); offset += 4;
|
|
233
|
+
const sy = buffer.readFloatBE(offset); offset += 4;
|
|
234
|
+
const sz = buffer.readFloatBE(offset); offset += 4;
|
|
235
|
+
const scale: [number, number, number] = [sx, sy, sz];
|
|
236
|
+
|
|
237
|
+
let velocity: [number, number, number] | undefined;
|
|
238
|
+
if (offset < buffer.length) {
|
|
239
|
+
const vx = buffer.readFloatBE(offset); offset += 4;
|
|
240
|
+
const vy = buffer.readFloatBE(offset); offset += 4;
|
|
241
|
+
const vz = buffer.readFloatBE(offset); offset += 4;
|
|
242
|
+
velocity = [vx, vy, vz];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
type: 'position_sync',
|
|
247
|
+
agent_id: agentId,
|
|
248
|
+
timestamp,
|
|
249
|
+
position,
|
|
250
|
+
rotation,
|
|
251
|
+
scale,
|
|
252
|
+
velocity,
|
|
253
|
+
};
|
|
254
|
+
} else if (typeCode === MessageTypeCode.FRAME_BUDGET) {
|
|
255
|
+
const frame_time_ms = buffer.readFloatBE(offset);
|
|
256
|
+
offset += 4;
|
|
257
|
+
const budget_remaining_ms = buffer.readFloatBE(offset);
|
|
258
|
+
offset += 4;
|
|
259
|
+
const target_fps = buffer.readFloatBE(offset);
|
|
260
|
+
offset += 4;
|
|
261
|
+
const actual_fps = buffer.readFloatBE(offset);
|
|
262
|
+
offset += 4;
|
|
263
|
+
const qualityCode = buffer.readUInt8(offset);
|
|
264
|
+
offset += 1;
|
|
265
|
+
const qualityLevels: Array<FrameBudgetMessage['quality_level']> = ['high', 'medium', 'low', 'minimal'];
|
|
266
|
+
const quality_level = qualityLevels[qualityCode] ?? 'medium';
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
type: 'frame_budget',
|
|
270
|
+
agent_id: agentId,
|
|
271
|
+
timestamp,
|
|
272
|
+
frame_time_ms,
|
|
273
|
+
budget_remaining_ms,
|
|
274
|
+
target_fps,
|
|
275
|
+
actual_fps,
|
|
276
|
+
quality_level,
|
|
277
|
+
};
|
|
278
|
+
} else {
|
|
279
|
+
// JSON-encoded message
|
|
280
|
+
const json = buffer.toString('utf-8', offset);
|
|
281
|
+
return JSON.parse(json) as RealTimeMessage;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// ============================================================================
|
|
286
|
+
// REAL-TIME TRANSPORT (UDP/WebRTC)
|
|
287
|
+
// ============================================================================
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Real-time transport interface
|
|
291
|
+
*/
|
|
292
|
+
export interface RealTimeTransport {
|
|
293
|
+
send(buffer: Buffer, targetAgent?: string): Promise<void>;
|
|
294
|
+
broadcast(buffer: Buffer): Promise<void>;
|
|
295
|
+
close(): Promise<void>;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* UDP-based real-time transport (Node.js environment)
|
|
300
|
+
*/
|
|
301
|
+
export class UDPRealTimeTransport implements RealTimeTransport {
|
|
302
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- dgram.Socket dynamically imported at runtime
|
|
303
|
+
private socket?: any;
|
|
304
|
+
private port: number;
|
|
305
|
+
private targetHost: string = 'localhost';
|
|
306
|
+
|
|
307
|
+
constructor(port: number) {
|
|
308
|
+
this.port = port;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async init(): Promise<void> {
|
|
312
|
+
// Dynamic import for Node.js dgram module
|
|
313
|
+
const dgram = await import('dgram');
|
|
314
|
+
this.socket = dgram.createSocket('udp4');
|
|
315
|
+
|
|
316
|
+
return new Promise((resolve, reject) => {
|
|
317
|
+
this.socket.bind(this.port, () => {
|
|
318
|
+
this.socket.setBroadcast(true);
|
|
319
|
+
resolve();
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
this.socket.on('error', (err: Error) => {
|
|
323
|
+
reject(err);
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
async send(buffer: Buffer, _targetAgent?: string): Promise<void> {
|
|
329
|
+
if (!this.socket) throw new Error('Transport not initialized');
|
|
330
|
+
|
|
331
|
+
return new Promise((resolve, reject) => {
|
|
332
|
+
// In production, would use agent registry to lookup agent address
|
|
333
|
+
// For now, send to local broadcast
|
|
334
|
+
this.socket.send(buffer, this.port, this.targetHost, (err: Error | null) => {
|
|
335
|
+
if (err) reject(err);
|
|
336
|
+
else resolve();
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
async broadcast(buffer: Buffer): Promise<void> {
|
|
342
|
+
if (!this.socket) throw new Error('Transport not initialized');
|
|
343
|
+
|
|
344
|
+
return new Promise((resolve, reject) => {
|
|
345
|
+
this.socket.send(buffer, this.port, '255.255.255.255', (err: Error | null) => {
|
|
346
|
+
if (err) reject(err);
|
|
347
|
+
else resolve();
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
async close(): Promise<void> {
|
|
353
|
+
if (this.socket) {
|
|
354
|
+
return new Promise((resolve) => {
|
|
355
|
+
this.socket.close(() => resolve());
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
onMessage(callback: (buffer: Buffer, rinfo: unknown) => void): void {
|
|
361
|
+
if (!this.socket) throw new Error('Transport not initialized');
|
|
362
|
+
this.socket.on('message', callback);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* WebRTC-based real-time transport (Browser environment)
|
|
368
|
+
*/
|
|
369
|
+
export class WebRTCRealTimeTransport implements RealTimeTransport {
|
|
370
|
+
private dataChannel?: RTCDataChannel;
|
|
371
|
+
private peerConnection?: RTCPeerConnection;
|
|
372
|
+
|
|
373
|
+
constructor(private config: RTCConfiguration) {}
|
|
374
|
+
|
|
375
|
+
async init(remoteDescription?: RTCSessionDescriptionInit): Promise<void> {
|
|
376
|
+
this.peerConnection = new RTCPeerConnection(this.config);
|
|
377
|
+
|
|
378
|
+
// Create data channel for real-time messages
|
|
379
|
+
this.dataChannel = this.peerConnection.createDataChannel('realtime', {
|
|
380
|
+
ordered: false, // Unordered for minimal latency
|
|
381
|
+
maxRetransmits: 0, // No retransmits (UDP-like)
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// Configure for low-latency
|
|
385
|
+
this.dataChannel.bufferedAmountLowThreshold = 0;
|
|
386
|
+
|
|
387
|
+
if (remoteDescription) {
|
|
388
|
+
await this.peerConnection.setRemoteDescription(remoteDescription);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async send(buffer: Buffer, _targetAgent?: string): Promise<void> {
|
|
393
|
+
if (!this.dataChannel || this.dataChannel.readyState !== 'open') {
|
|
394
|
+
throw new Error('Data channel not ready');
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
this.dataChannel.send(new Uint8Array(buffer));
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async broadcast(buffer: Buffer): Promise<void> {
|
|
401
|
+
// WebRTC broadcast would require multiple peer connections
|
|
402
|
+
// For now, just send to the connected peer
|
|
403
|
+
return this.send(buffer);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
async close(): Promise<void> {
|
|
407
|
+
if (this.dataChannel) {
|
|
408
|
+
this.dataChannel.close();
|
|
409
|
+
}
|
|
410
|
+
if (this.peerConnection) {
|
|
411
|
+
this.peerConnection.close();
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
onMessage(callback: (buffer: Buffer) => void): void {
|
|
416
|
+
if (!this.dataChannel) throw new Error('Data channel not initialized');
|
|
417
|
+
|
|
418
|
+
this.dataChannel.onmessage = (event: MessageEvent) => {
|
|
419
|
+
// Convert ArrayBuffer to Buffer
|
|
420
|
+
const buffer = Buffer.from(event.data);
|
|
421
|
+
callback(buffer);
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
async createOffer(): Promise<RTCSessionDescriptionInit> {
|
|
426
|
+
if (!this.peerConnection) throw new Error('Peer connection not initialized');
|
|
427
|
+
|
|
428
|
+
const offer = await this.peerConnection.createOffer();
|
|
429
|
+
await this.peerConnection.setLocalDescription(offer);
|
|
430
|
+
return offer;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
async createAnswer(): Promise<RTCSessionDescriptionInit> {
|
|
434
|
+
if (!this.peerConnection) throw new Error('Peer connection not initialized');
|
|
435
|
+
|
|
436
|
+
const answer = await this.peerConnection.createAnswer();
|
|
437
|
+
await this.peerConnection.setLocalDescription(answer);
|
|
438
|
+
return answer;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// ============================================================================
|
|
443
|
+
// LAYER 1 CLIENT
|
|
444
|
+
// ============================================================================
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Layer 1 Real-Time Communication Client
|
|
448
|
+
*/
|
|
449
|
+
export class Layer1RealTimeClient extends EventEmitter {
|
|
450
|
+
private config: RealTimeProtocolConfig;
|
|
451
|
+
private transport?: RealTimeTransport;
|
|
452
|
+
private agentId: string;
|
|
453
|
+
private messageCount = 0;
|
|
454
|
+
private lastMessageTime = 0;
|
|
455
|
+
private readonly _messageBuffer: RealTimeMessage[] = [];
|
|
456
|
+
|
|
457
|
+
constructor(agentId: string, config?: Partial<RealTimeProtocolConfig>) {
|
|
458
|
+
super();
|
|
459
|
+
this.agentId = agentId;
|
|
460
|
+
this.config = { ...DEFAULT_REALTIME_CONFIG, ...config } as RealTimeProtocolConfig;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Initialize transport and start listening
|
|
465
|
+
*/
|
|
466
|
+
async init(useWebRTC = false): Promise<void> {
|
|
467
|
+
if (useWebRTC) {
|
|
468
|
+
const transport = new WebRTCRealTimeTransport(
|
|
469
|
+
this.config.webrtc?.iceServers ? { iceServers: this.config.webrtc.iceServers } : {}
|
|
470
|
+
);
|
|
471
|
+
await transport.init();
|
|
472
|
+
this.transport = transport;
|
|
473
|
+
|
|
474
|
+
transport.onMessage((buffer) => {
|
|
475
|
+
this.handleIncomingMessage(buffer);
|
|
476
|
+
});
|
|
477
|
+
} else {
|
|
478
|
+
const transport = new UDPRealTimeTransport(this.config.udpPort || 9001);
|
|
479
|
+
await transport.init();
|
|
480
|
+
this.transport = transport;
|
|
481
|
+
|
|
482
|
+
transport.onMessage((buffer) => {
|
|
483
|
+
this.handleIncomingMessage(buffer);
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Send real-time message
|
|
490
|
+
*/
|
|
491
|
+
async send(
|
|
492
|
+
message: RealTimeMessageBody,
|
|
493
|
+
targetAgent?: string
|
|
494
|
+
): Promise<void> {
|
|
495
|
+
if (!this.transport) throw new Error('Transport not initialized');
|
|
496
|
+
|
|
497
|
+
// Add agent ID and timestamp
|
|
498
|
+
const fullMessage: RealTimeMessage = {
|
|
499
|
+
...message,
|
|
500
|
+
agent_id: this.agentId,
|
|
501
|
+
timestamp: this.getMicroseconds(),
|
|
502
|
+
} as RealTimeMessage;
|
|
503
|
+
|
|
504
|
+
// Encode to binary
|
|
505
|
+
const buffer = this.config.binary
|
|
506
|
+
? encodeRealTimeMessage(fullMessage)
|
|
507
|
+
: Buffer.from(JSON.stringify(fullMessage), 'utf-8');
|
|
508
|
+
|
|
509
|
+
// Check message size
|
|
510
|
+
if (buffer.length > this.config.maxMessageSize) {
|
|
511
|
+
throw new Error(`Message size ${buffer.length} exceeds max ${this.config.maxMessageSize}`);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Rate limiting
|
|
515
|
+
await this.enforceRateLimit();
|
|
516
|
+
|
|
517
|
+
// Send
|
|
518
|
+
if (targetAgent) {
|
|
519
|
+
await this.transport.send(buffer, targetAgent);
|
|
520
|
+
} else {
|
|
521
|
+
await this.transport.broadcast(buffer);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
this.messageCount++;
|
|
525
|
+
this.lastMessageTime = Date.now();
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Send position sync message
|
|
530
|
+
*/
|
|
531
|
+
async sendPositionSync(
|
|
532
|
+
position: [number, number, number],
|
|
533
|
+
rotation: [number, number, number, number],
|
|
534
|
+
scale: [number, number, number],
|
|
535
|
+
velocity?: [number, number, number]
|
|
536
|
+
): Promise<void> {
|
|
537
|
+
await this.send({
|
|
538
|
+
type: 'position_sync',
|
|
539
|
+
position,
|
|
540
|
+
rotation,
|
|
541
|
+
scale,
|
|
542
|
+
velocity,
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Send frame budget message
|
|
548
|
+
*/
|
|
549
|
+
async sendFrameBudget(
|
|
550
|
+
frameTimeMs: number,
|
|
551
|
+
budgetRemainingMs: number,
|
|
552
|
+
targetFps: number,
|
|
553
|
+
actualFps: number,
|
|
554
|
+
qualityLevel: 'high' | 'medium' | 'low' | 'minimal'
|
|
555
|
+
): Promise<void> {
|
|
556
|
+
await this.send({
|
|
557
|
+
type: 'frame_budget',
|
|
558
|
+
frame_time_ms: frameTimeMs,
|
|
559
|
+
budget_remaining_ms: budgetRemainingMs,
|
|
560
|
+
target_fps: targetFps,
|
|
561
|
+
actual_fps: actualFps,
|
|
562
|
+
quality_level: qualityLevel,
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Close transport
|
|
568
|
+
*/
|
|
569
|
+
async close(): Promise<void> {
|
|
570
|
+
if (this.transport) {
|
|
571
|
+
await this.transport.close();
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Handle incoming message
|
|
577
|
+
*/
|
|
578
|
+
private handleIncomingMessage(buffer: Buffer): void {
|
|
579
|
+
try {
|
|
580
|
+
const message = this.config.binary
|
|
581
|
+
? decodeRealTimeMessage(buffer)
|
|
582
|
+
: (JSON.parse(buffer.toString('utf-8')) as RealTimeMessage);
|
|
583
|
+
|
|
584
|
+
// Emit typed events
|
|
585
|
+
this.emit('message', message);
|
|
586
|
+
this.emit(message.type, message);
|
|
587
|
+
|
|
588
|
+
// Calculate latency
|
|
589
|
+
const latency = (this.getMicroseconds() - message.timestamp) / 1000; // ms
|
|
590
|
+
this.emit('latency', latency);
|
|
591
|
+
|
|
592
|
+
if (latency > this.config.targetLatency) {
|
|
593
|
+
this.emit('latency_warning', { message, latency });
|
|
594
|
+
}
|
|
595
|
+
} catch (err) {
|
|
596
|
+
this.emit('error', err);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Enforce rate limiting
|
|
602
|
+
*/
|
|
603
|
+
private async enforceRateLimit(): Promise<void> {
|
|
604
|
+
const now = Date.now();
|
|
605
|
+
const timeSinceLastMessage = now - this.lastMessageTime;
|
|
606
|
+
const minInterval = 1000 / this.config.messagesPerSecond;
|
|
607
|
+
|
|
608
|
+
if (timeSinceLastMessage < minInterval) {
|
|
609
|
+
const delay = minInterval - timeSinceLastMessage;
|
|
610
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Get current time in microseconds
|
|
616
|
+
*/
|
|
617
|
+
private getMicroseconds(): number {
|
|
618
|
+
const hrTime = process.hrtime ? process.hrtime() : [Date.now() / 1000, 0];
|
|
619
|
+
return hrTime[0] * 1000000 + Math.floor(hrTime[1] / 1000);
|
|
620
|
+
}
|
|
621
|
+
}
|