@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,313 @@
1
+ /**
2
+ * PromptTemplates — Production Tests
3
+ *
4
+ * Tests: PromptTemplateSystem (getAvailableTemplates, getTemplatesByCategory, getTemplate,
5
+ * createPrompt, validateContext, registerTemplate, suggestTemplates, getCategories, createBatch)
6
+ * and QuickPrompts (object, interactive, physics, player, networked).
7
+ */
8
+
9
+ import { describe, it, expect, beforeEach } from 'vitest';
10
+ import { PromptTemplateSystem, QuickPrompts } from '../PromptTemplates';
11
+ import type { PromptTemplate, TemplateContext } from '../PromptTemplates';
12
+
13
+ function makeCustomTemplate(overrides: Partial<PromptTemplate> = {}): PromptTemplate {
14
+ return {
15
+ id: 'custom-001',
16
+ name: 'Custom Template',
17
+ description: 'A custom test template',
18
+ category: 'custom',
19
+ template: 'Create a {{shape}} with {{color}}',
20
+ variables: ['shape', 'color'],
21
+ examples: ['Create a box with red'],
22
+ bestFor: 'testing',
23
+ ...overrides,
24
+ };
25
+ }
26
+
27
+ describe('PromptTemplateSystem — getAvailableTemplates', () => {
28
+ let pts: PromptTemplateSystem;
29
+ beforeEach(() => {
30
+ pts = new PromptTemplateSystem();
31
+ });
32
+
33
+ it('returns a non-empty array of built-in templates', () => {
34
+ const templates = pts.getAvailableTemplates();
35
+ expect(templates.length).toBeGreaterThan(0);
36
+ });
37
+
38
+ it('each template has required fields', () => {
39
+ for (const t of pts.getAvailableTemplates()) {
40
+ expect(typeof t.id).toBe('string');
41
+ expect(typeof t.name).toBe('string');
42
+ expect(typeof t.category).toBe('string');
43
+ expect(Array.isArray(t.variables)).toBe(true);
44
+ expect(Array.isArray(t.examples)).toBe(true);
45
+ }
46
+ });
47
+
48
+ it('includes built-in basic-object template', () => {
49
+ const ids = pts.getAvailableTemplates().map((t) => t.id);
50
+ expect(ids).toContain('basic-object');
51
+ });
52
+
53
+ it('custom templates are included after registration', () => {
54
+ pts.registerTemplate(makeCustomTemplate());
55
+ const ids = pts.getAvailableTemplates().map((t) => t.id);
56
+ expect(ids).toContain('custom-001');
57
+ });
58
+ });
59
+
60
+ describe('PromptTemplateSystem — getTemplatesByCategory', () => {
61
+ let pts: PromptTemplateSystem;
62
+ beforeEach(() => {
63
+ pts = new PromptTemplateSystem();
64
+ });
65
+
66
+ it('returns templates for "basic" category', () => {
67
+ const results = pts.getTemplatesByCategory('basic');
68
+ expect(results.length).toBeGreaterThan(0);
69
+ expect(results.every((t) => t.category === 'basic')).toBe(true);
70
+ });
71
+
72
+ it('returns empty array for unknown category', () => {
73
+ expect(pts.getTemplatesByCategory('nonexistent')).toHaveLength(0);
74
+ });
75
+
76
+ it('returns ui category templates only', () => {
77
+ const results = pts.getTemplatesByCategory('ui');
78
+ expect(results.every((t) => t.category === 'ui')).toBe(true);
79
+ expect(results.length).toBeGreaterThan(0);
80
+ });
81
+ });
82
+
83
+ describe('PromptTemplateSystem — getTemplate', () => {
84
+ let pts: PromptTemplateSystem;
85
+ beforeEach(() => {
86
+ pts = new PromptTemplateSystem();
87
+ });
88
+
89
+ it('returns a built-in template by id', () => {
90
+ const t = pts.getTemplate('basic-object');
91
+ expect(t).not.toBeNull();
92
+ expect(t!.id).toBe('basic-object');
93
+ });
94
+
95
+ it('returns null for unknown id', () => {
96
+ expect(pts.getTemplate('does-not-exist')).toBeNull();
97
+ });
98
+
99
+ it('returns registered custom template', () => {
100
+ pts.registerTemplate(makeCustomTemplate({ id: 'my-custom' }));
101
+ expect(pts.getTemplate('my-custom')).not.toBeNull();
102
+ });
103
+
104
+ it('custom template takes precedence (same id would override)', () => {
105
+ pts.registerTemplate(makeCustomTemplate({ id: 'physics-object', name: 'Override' }));
106
+ const t = pts.getTemplate('physics-object');
107
+ // custom map is checked after built-in; built-in wins due to || chain
108
+ expect(t).not.toBeNull();
109
+ });
110
+ });
111
+
112
+ describe('PromptTemplateSystem — createPrompt', () => {
113
+ let pts: PromptTemplateSystem;
114
+ beforeEach(() => {
115
+ pts = new PromptTemplateSystem();
116
+ });
117
+
118
+ it('replaces all variables in template', () => {
119
+ const result = pts.createPrompt('basic-object', {
120
+ geometry: 'sphere',
121
+ color: 'red',
122
+ position: '[0,1,0]',
123
+ });
124
+ expect(result).toContain('sphere');
125
+ expect(result).toContain('red');
126
+ expect(result).toContain('[0,1,0]');
127
+ expect(result).not.toContain('{{');
128
+ });
129
+
130
+ it('throws for unknown template id', () => {
131
+ expect(() => pts.createPrompt('no-such-template', {})).toThrow();
132
+ });
133
+
134
+ it('leaves un-provided variables as placeholders', () => {
135
+ const result = pts.createPrompt('basic-object', { geometry: 'cube' });
136
+ expect(result).toContain('{{color}}');
137
+ });
138
+
139
+ it('handles number values in context', () => {
140
+ const result = pts.createPrompt('physics-object', {
141
+ geometry: 'sphere',
142
+ physics_type: 'dynamic',
143
+ mass: 2.5,
144
+ restitution: 0.7,
145
+ });
146
+ expect(result).toContain('2.5');
147
+ expect(result).toContain('0.7');
148
+ });
149
+
150
+ it('handles boolean values in context', () => {
151
+ pts.registerTemplate(makeCustomTemplate({ template: 'Flag: {{flag}}', variables: ['flag'] }));
152
+ const result = pts.createPrompt('custom-001', { flag: true });
153
+ expect(result).toContain('true');
154
+ });
155
+
156
+ it('replaces multiple occurrences of same variable', () => {
157
+ pts.registerTemplate(makeCustomTemplate({ template: '{{x}} and {{x}}', variables: ['x'] }));
158
+ const result = pts.createPrompt('custom-001', { x: 'HELLO' });
159
+ expect(result).toBe('HELLO and HELLO');
160
+ });
161
+ });
162
+
163
+ describe('PromptTemplateSystem — validateContext', () => {
164
+ let pts: PromptTemplateSystem;
165
+ beforeEach(() => {
166
+ pts = new PromptTemplateSystem();
167
+ });
168
+
169
+ it('valid=true when all variables provided', () => {
170
+ const { valid, missing } = pts.validateContext('basic-object', {
171
+ geometry: 'cube',
172
+ color: 'red',
173
+ position: '[0,0,0]',
174
+ });
175
+ expect(valid).toBe(true);
176
+ expect(missing).toHaveLength(0);
177
+ });
178
+
179
+ it('valid=false when variables are missing', () => {
180
+ const { valid, missing } = pts.validateContext('basic-object', { geometry: 'cube' });
181
+ expect(valid).toBe(false);
182
+ expect(missing).toContain('color');
183
+ expect(missing).toContain('position');
184
+ });
185
+
186
+ it('returns valid=false for unknown template', () => {
187
+ const { valid, missing } = pts.validateContext('bogus', {});
188
+ expect(valid).toBe(false);
189
+ expect(missing.length).toBeGreaterThan(0);
190
+ });
191
+
192
+ it('extra context keys do not cause false positive', () => {
193
+ const { valid } = pts.validateContext('basic-object', {
194
+ geometry: 'cube',
195
+ color: 'blue',
196
+ position: '[0,0,0]',
197
+ extra: 'ignored',
198
+ });
199
+ expect(valid).toBe(true);
200
+ });
201
+ });
202
+
203
+ describe('PromptTemplateSystem — registerTemplate / getCategories / suggestTemplates', () => {
204
+ let pts: PromptTemplateSystem;
205
+ beforeEach(() => {
206
+ pts = new PromptTemplateSystem();
207
+ });
208
+
209
+ it('getCategories returns sorted unique category names', () => {
210
+ const cats = pts.getCategories();
211
+ expect(cats.length).toBeGreaterThan(0);
212
+ const sorted = [...cats].sort();
213
+ expect(cats).toEqual(sorted);
214
+ expect(new Set(cats).size).toBe(cats.length);
215
+ });
216
+
217
+ it('custom category appears in getCategories after registration', () => {
218
+ pts.registerTemplate(makeCustomTemplate({ category: 'unicorn' }));
219
+ expect(pts.getCategories()).toContain('unicorn');
220
+ });
221
+
222
+ it('suggestTemplates returns subset matching query', () => {
223
+ const results = pts.suggestTemplates('basic', 'object');
224
+ expect(results.length).toBeLessThanOrEqual(5);
225
+ expect(results.every((t) => t.category === 'basic')).toBe(true);
226
+ });
227
+
228
+ it('suggestTemplates returns all in category when no query', () => {
229
+ const results = pts.suggestTemplates('basic');
230
+ expect(results.length).toBeGreaterThan(0);
231
+ });
232
+
233
+ it('suggestTemplates returns empty for unknown category', () => {
234
+ expect(pts.suggestTemplates('noop')).toHaveLength(0);
235
+ });
236
+ });
237
+
238
+ describe('PromptTemplateSystem — createBatch', () => {
239
+ let pts: PromptTemplateSystem;
240
+ beforeEach(() => {
241
+ pts = new PromptTemplateSystem();
242
+ });
243
+
244
+ it('returns array of same length as input', () => {
245
+ const batch = pts.createBatch([
246
+ {
247
+ templateId: 'basic-object',
248
+ context: { geometry: 'box', color: 'blue', position: '[0,0,0]' },
249
+ },
250
+ {
251
+ templateId: 'basic-object',
252
+ context: { geometry: 'sphere', color: 'red', position: '[1,0,0]' },
253
+ },
254
+ ]);
255
+ expect(batch).toHaveLength(2);
256
+ });
257
+
258
+ it('each result is a string with variables substituted', () => {
259
+ const batch = pts.createBatch([
260
+ {
261
+ templateId: 'basic-object',
262
+ context: { geometry: 'cube', color: 'green', position: '[0,0,0]' },
263
+ },
264
+ ]);
265
+ expect(batch[0]).toContain('cube');
266
+ expect(batch[0]).toContain('green');
267
+ });
268
+
269
+ it('returns empty array for empty input', () => {
270
+ expect(pts.createBatch([])).toHaveLength(0);
271
+ });
272
+ });
273
+
274
+ describe('QuickPrompts', () => {
275
+ it('object() returns string containing geometry and color', () => {
276
+ const p = QuickPrompts.object('sphere', 'blue', [0, 1, 0]);
277
+ expect(typeof p).toBe('string');
278
+ expect(p).toContain('sphere');
279
+ expect(p).toContain('blue');
280
+ });
281
+
282
+ it('object() encodes position as JSON', () => {
283
+ const p = QuickPrompts.object('cube', 'red', [1, 2, 3]);
284
+ expect(p).toContain('[1,2,3]');
285
+ });
286
+
287
+ it('interactive() returns string with all provided params', () => {
288
+ const p = QuickPrompts.interactive('green', 'cube', 'responds to touch', 'feedback');
289
+ expect(p).toContain('green');
290
+ expect(p).toContain('cube');
291
+ expect(p).toContain('responds to touch');
292
+ expect(p).toContain('feedback');
293
+ });
294
+
295
+ it('physics() returns string with mass and restitution', () => {
296
+ const p = QuickPrompts.physics('sphere', 'dynamic', 2.5, 0.8);
297
+ expect(p).toContain('2.5');
298
+ expect(p).toContain('0.8');
299
+ });
300
+
301
+ it('player() returns string with movement type and health', () => {
302
+ const p = QuickPrompts.player('humanoid', 'WASD', 100, 'jump,dash');
303
+ expect(p).toContain('WASD');
304
+ expect(p).toContain('100');
305
+ expect(p).toContain('jump,dash');
306
+ });
307
+
308
+ it('networked() includes owner_type default first-grabber', () => {
309
+ const p = QuickPrompts.networked('ball', 'position,rotation', 20);
310
+ expect(p).toContain('first-grabber');
311
+ expect(p).toContain('20');
312
+ });
313
+ });
@@ -0,0 +1,146 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { PromptTemplateSystem, QuickPrompts } from '../PromptTemplates';
3
+
4
+ describe('PromptTemplateSystem', () => {
5
+ let sys: PromptTemplateSystem;
6
+
7
+ beforeEach(() => {
8
+ sys = new PromptTemplateSystem();
9
+ });
10
+
11
+ // Built-in templates
12
+ it('getAvailableTemplates returns built-ins', () => {
13
+ const templates = sys.getAvailableTemplates();
14
+ expect(templates.length).toBeGreaterThan(0);
15
+ expect(templates.some((t) => t.id === 'basic-object')).toBe(true);
16
+ });
17
+
18
+ it('getTemplate returns specific template', () => {
19
+ const t = sys.getTemplate('basic-object');
20
+ expect(t).not.toBeNull();
21
+ expect(t!.category).toBe('basic');
22
+ });
23
+
24
+ it('getTemplate returns null for unknown', () => {
25
+ expect(sys.getTemplate('nonexistent')).toBeNull();
26
+ });
27
+
28
+ // Category
29
+ it('getTemplatesByCategory filters', () => {
30
+ const basic = sys.getTemplatesByCategory('basic');
31
+ expect(basic.length).toBeGreaterThan(0);
32
+ expect(basic.every((t) => t.category === 'basic')).toBe(true);
33
+ });
34
+
35
+ it('getCategories returns unique categories', () => {
36
+ const cats = sys.getCategories();
37
+ expect(cats.length).toBeGreaterThan(0);
38
+ expect(new Set(cats).size).toBe(cats.length);
39
+ });
40
+
41
+ // Prompt creation
42
+ it('createPrompt interpolates variables', () => {
43
+ const prompt = sys.createPrompt('basic-object', {
44
+ color: 'blue',
45
+ geometry: 'sphere',
46
+ position: '[0,1,0]',
47
+ });
48
+ expect(prompt).toContain('blue');
49
+ expect(prompt).toContain('sphere');
50
+ });
51
+
52
+ it('createPrompt leaves unmatched variables as placeholders', () => {
53
+ const prompt = sys.createPrompt('basic-object', { color: 'red' });
54
+ // Missing variables remain as {{var}}
55
+ expect(prompt).toContain('red');
56
+ });
57
+
58
+ // Validation
59
+ it('validateContext reports missing variables', () => {
60
+ const result = sys.validateContext('basic-object', {});
61
+ expect(result.valid).toBe(false);
62
+ expect(result.missing.length).toBeGreaterThan(0);
63
+ });
64
+
65
+ it('validateContext passes with all variables', () => {
66
+ const t = sys.getTemplate('basic-object')!;
67
+ const context: Record<string, string> = {};
68
+ for (const v of t.variables) context[v] = 'test';
69
+ const result = sys.validateContext('basic-object', context);
70
+ expect(result.valid).toBe(true);
71
+ expect(result.missing.length).toBe(0);
72
+ });
73
+
74
+ // Custom templates
75
+ it('registerTemplate adds custom template', () => {
76
+ sys.registerTemplate({
77
+ id: 'custom',
78
+ name: 'Custom',
79
+ description: 'Test',
80
+ category: 'custom',
81
+ template: 'Hello {{name}}',
82
+ variables: ['name'],
83
+ examples: [],
84
+ bestFor: 'test',
85
+ });
86
+ expect(sys.getTemplate('custom')).not.toBeNull();
87
+ });
88
+
89
+ // Suggest
90
+ it('suggestTemplates by category', () => {
91
+ const suggestions = sys.suggestTemplates('physics');
92
+ expect(suggestions.length).toBeGreaterThan(0);
93
+ });
94
+
95
+ it('suggestTemplates with query filters by name/description', () => {
96
+ const suggestions = sys.suggestTemplates('character', 'enemy');
97
+ expect(suggestions.some((t) => t.name.toLowerCase().includes('enemy'))).toBe(true);
98
+ });
99
+
100
+ // Batch
101
+ it('createBatch creates multiple prompts', () => {
102
+ const prompts = sys.createBatch([
103
+ {
104
+ templateId: 'basic-object',
105
+ context: { color: 'red', geometry: 'cube', interaction: 'grab', purpose: 'demo' },
106
+ },
107
+ {
108
+ templateId: 'basic-object',
109
+ context: { color: 'blue', geometry: 'sphere', interaction: 'touch', purpose: 'ui' },
110
+ },
111
+ ]);
112
+ expect(prompts.length).toBe(2);
113
+ expect(prompts[0]).toContain('red');
114
+ expect(prompts[1]).toContain('blue');
115
+ });
116
+ });
117
+
118
+ describe('QuickPrompts', () => {
119
+ it('object creates interpolated prompt', () => {
120
+ const p = QuickPrompts.object('sphere', 'blue', [0, 1, 0]);
121
+ expect(p).toContain('sphere');
122
+ expect(p).toContain('blue');
123
+ });
124
+
125
+ it('interactive creates prompt', () => {
126
+ const p = QuickPrompts.interactive('red', 'cube', 'hover', 'feedback');
127
+ expect(p).toContain('red');
128
+ expect(p).toContain('hover');
129
+ });
130
+
131
+ it('physics creates prompt', () => {
132
+ const p = QuickPrompts.physics('box', 'dynamic', 5, 0.5);
133
+ expect(p).toContain('box');
134
+ expect(p).toContain('dynamic');
135
+ });
136
+
137
+ it('player creates prompt', () => {
138
+ const p = QuickPrompts.player('capsule', 'fps', 100, 'jump');
139
+ expect(p).toContain('capsule');
140
+ });
141
+
142
+ it('networked creates prompt', () => {
143
+ const p = QuickPrompts.networked('ball', 'position,rotation', 30);
144
+ expect(p).toContain('ball');
145
+ });
146
+ });
@@ -0,0 +1,37 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { SemanticSearchService } from '../SemanticSearchService';
3
+ import type { AIAdapter } from '../AIAdapter';
4
+
5
+ describe('SemanticSearchService', () => {
6
+ const mockTraits = [
7
+ { name: 'grabbable', description: 'Makes object grabbable by VR controllers.' },
8
+ { name: 'physics', description: 'Full physics simulation with gravity.' },
9
+ { name: 'glowing', description: 'Object emits a glow effect.' },
10
+ ];
11
+
12
+ it('should rank traits by similarity', async () => {
13
+ const mockAdapter: AIAdapter = {
14
+ id: 'mock',
15
+ name: 'Mock Adapter',
16
+ isReady: () => true,
17
+ getEmbeddings: async (text: string | string[]) => {
18
+ const texts = Array.isArray(text) ? text : [text];
19
+ return texts.map((t) => {
20
+ if (t.includes('grab')) return [1, 0, 0];
21
+ if (t.includes('physics')) return [0, 1, 0];
22
+ if (t.includes('glow')) return [0, 0, 1];
23
+ if (t.includes('hand')) return [0.9, 0, 0]; // Close to grab
24
+ return [0, 0, 0];
25
+ });
26
+ },
27
+ };
28
+
29
+ const searchService = new SemanticSearchService(mockAdapter, mockTraits);
30
+ await searchService.initialize();
31
+
32
+ const results = await searchService.search('something to pick up with my hand');
33
+
34
+ expect(results[0].item.name).toBe('grabbable');
35
+ expect(results[0].score).toBeGreaterThan(0.8);
36
+ });
37
+ });
@@ -0,0 +1,162 @@
1
+ /**
2
+ * StateMachine — Production Test Suite
3
+ *
4
+ * Covers: state management, transitions, guards, enter/exit hooks,
5
+ * update cycle, context, history, hierarchy (child states).
6
+ */
7
+ import { describe, it, expect, vi } from 'vitest';
8
+ import { StateMachine } from '../StateMachine';
9
+
10
+ describe('StateMachine — Production', () => {
11
+ // ─── State Management ─────────────────────────────────────────────
12
+ it('addState + getCurrentState', () => {
13
+ const sm = new StateMachine();
14
+ sm.addState({ id: 'idle' });
15
+ sm.setInitialState('idle');
16
+ expect(sm.getCurrentState()).toBe('idle');
17
+ });
18
+
19
+ it('no initial state → null', () => {
20
+ const sm = new StateMachine();
21
+ expect(sm.getCurrentState()).toBeNull();
22
+ });
23
+
24
+ it('getStateCount tracks states', () => {
25
+ const sm = new StateMachine();
26
+ sm.addState({ id: 'a' });
27
+ sm.addState({ id: 'b' });
28
+ expect(sm.getStateCount()).toBe(2);
29
+ });
30
+
31
+ it('removeState removes state', () => {
32
+ const sm = new StateMachine();
33
+ sm.addState({ id: 'temp' });
34
+ sm.removeState('temp');
35
+ expect(sm.getStateCount()).toBe(0);
36
+ });
37
+
38
+ // ─── Transitions ──────────────────────────────────────────────────
39
+ it('send transitions between states', () => {
40
+ const sm = new StateMachine();
41
+ sm.addState({ id: 'idle' });
42
+ sm.addState({ id: 'walking' });
43
+ sm.addTransition({ from: 'idle', to: 'walking', event: 'MOVE' });
44
+ sm.setInitialState('idle');
45
+ const result = sm.send('MOVE');
46
+ expect(result).toBe(true);
47
+ expect(sm.getCurrentState()).toBe('walking');
48
+ });
49
+
50
+ it('send returns false for invalid event', () => {
51
+ const sm = new StateMachine();
52
+ sm.addState({ id: 'idle' });
53
+ sm.setInitialState('idle');
54
+ expect(sm.send('NONEXISTENT')).toBe(false);
55
+ });
56
+
57
+ it('send returns false when no current state', () => {
58
+ const sm = new StateMachine();
59
+ expect(sm.send('X')).toBe(false);
60
+ });
61
+
62
+ // ─── Guards ───────────────────────────────────────────────────────
63
+ it('guard blocks transition when false', () => {
64
+ const sm = new StateMachine();
65
+ sm.addState({ id: 'idle' });
66
+ sm.addState({ id: 'attack' });
67
+ sm.addTransition({ from: 'idle', to: 'attack', event: 'FIGHT', guard: () => false });
68
+ sm.setInitialState('idle');
69
+ expect(sm.send('FIGHT')).toBe(false);
70
+ expect(sm.getCurrentState()).toBe('idle');
71
+ });
72
+
73
+ it('guard allows transition when true', () => {
74
+ const sm = new StateMachine();
75
+ sm.addState({ id: 'idle' });
76
+ sm.addState({ id: 'attack' });
77
+ sm.addTransition({ from: 'idle', to: 'attack', event: 'FIGHT', guard: () => true });
78
+ sm.setInitialState('idle');
79
+ expect(sm.send('FIGHT')).toBe(true);
80
+ expect(sm.getCurrentState()).toBe('attack');
81
+ });
82
+
83
+ // ─── Enter / Exit Hooks ───────────────────────────────────────────
84
+ it('onEnter fires when entering state', () => {
85
+ const spy = vi.fn();
86
+ const sm = new StateMachine();
87
+ sm.addState({ id: 'idle' });
88
+ sm.addState({ id: 'active', onEnter: spy });
89
+ sm.addTransition({ from: 'idle', to: 'active', event: 'GO' });
90
+ sm.setInitialState('idle');
91
+ sm.send('GO');
92
+ expect(spy).toHaveBeenCalled();
93
+ });
94
+
95
+ it('onExit fires when leaving state', () => {
96
+ const spy = vi.fn();
97
+ const sm = new StateMachine();
98
+ sm.addState({ id: 'idle', onExit: spy });
99
+ sm.addState({ id: 'active' });
100
+ sm.addTransition({ from: 'idle', to: 'active', event: 'GO' });
101
+ sm.setInitialState('idle');
102
+ sm.send('GO');
103
+ expect(spy).toHaveBeenCalled();
104
+ });
105
+
106
+ it('setInitialState fires onEnter', () => {
107
+ const spy = vi.fn();
108
+ const sm = new StateMachine();
109
+ sm.addState({ id: 'boot', onEnter: spy });
110
+ sm.setInitialState('boot');
111
+ expect(spy).toHaveBeenCalled();
112
+ });
113
+
114
+ // ─── Update ───────────────────────────────────────────────────────
115
+ it('update calls onUpdate for current state', () => {
116
+ const spy = vi.fn();
117
+ const sm = new StateMachine();
118
+ sm.addState({ id: 'idle', onUpdate: spy });
119
+ sm.setInitialState('idle');
120
+ sm.update();
121
+ expect(spy).toHaveBeenCalled();
122
+ });
123
+
124
+ // ─── Context ──────────────────────────────────────────────────────
125
+ it('setContext + getContext', () => {
126
+ const sm = new StateMachine();
127
+ sm.setContext('speed', 5);
128
+ expect(sm.getContext('speed')).toBe(5);
129
+ });
130
+
131
+ // ─── History ──────────────────────────────────────────────────────
132
+ it('getHistory tracks state transitions', () => {
133
+ const sm = new StateMachine();
134
+ sm.addState({ id: 'a' });
135
+ sm.addState({ id: 'b' });
136
+ sm.addTransition({ from: 'a', to: 'b', event: 'GO' });
137
+ sm.setInitialState('a');
138
+ sm.send('GO');
139
+ const history = sm.getHistory();
140
+ expect(history).toEqual(['a', 'b']);
141
+ });
142
+
143
+ // ─── isInState ────────────────────────────────────────────────────
144
+ it('isInState returns correct boolean', () => {
145
+ const sm = new StateMachine();
146
+ sm.addState({ id: 'idle' });
147
+ sm.setInitialState('idle');
148
+ expect(sm.isInState('idle')).toBe(true);
149
+ expect(sm.isInState('other')).toBe(false);
150
+ });
151
+
152
+ // ─── Hierarchy (Child States) ─────────────────────────────────────
153
+ it('getChildStates returns children', () => {
154
+ const sm = new StateMachine();
155
+ sm.addState({ id: 'combat' });
156
+ sm.addState({ id: 'melee', parent: 'combat' });
157
+ sm.addState({ id: 'ranged', parent: 'combat' });
158
+ sm.addState({ id: 'idle' });
159
+ const children = sm.getChildStates('combat');
160
+ expect(children.sort()).toEqual(['melee', 'ranged']);
161
+ });
162
+ });