@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,183 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
2
+ import { Blackboard } from '../Blackboard';
3
+
4
+ describe('Blackboard', () => {
5
+ let bb: Blackboard;
6
+
7
+ beforeEach(() => {
8
+ bb = new Blackboard();
9
+ });
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Get / Set / Has / Delete
13
+ // ---------------------------------------------------------------------------
14
+
15
+ it('set and get round-trip', () => {
16
+ bb.set('hp', 100);
17
+ expect(bb.get('hp')).toBe(100);
18
+ });
19
+
20
+ it('get returns undefined for missing key', () => {
21
+ expect(bb.get('nope')).toBeUndefined();
22
+ });
23
+
24
+ it('has checks key existence', () => {
25
+ bb.set('hp', 100);
26
+ expect(bb.has('hp')).toBe(true);
27
+ expect(bb.has('mp')).toBe(false);
28
+ });
29
+
30
+ it('delete removes key', () => {
31
+ bb.set('hp', 100);
32
+ expect(bb.delete('hp')).toBe(true);
33
+ expect(bb.has('hp')).toBe(false);
34
+ });
35
+
36
+ it('delete returns false for missing key', () => {
37
+ expect(bb.delete('nope')).toBe(false);
38
+ });
39
+
40
+ it('overwrite existing key', () => {
41
+ bb.set('hp', 100);
42
+ bb.set('hp', 50);
43
+ expect(bb.get('hp')).toBe(50);
44
+ });
45
+
46
+ // ---------------------------------------------------------------------------
47
+ // Scopes
48
+ // ---------------------------------------------------------------------------
49
+
50
+ it('set with scope groups keys', () => {
51
+ bb.set('agentHp', 100, 'agent');
52
+ bb.set('agentMp', 50, 'agent');
53
+ bb.set('envTemp', 25, 'env');
54
+ const agentData = bb.getByScope('agent');
55
+ expect(agentData.size).toBe(2);
56
+ expect(agentData.get('agentHp')).toBe(100);
57
+ });
58
+
59
+ it('getByScope returns empty for unknown scope', () => {
60
+ expect(bb.getByScope('nope').size).toBe(0);
61
+ });
62
+
63
+ it('clearScope removes all keys in scope', () => {
64
+ bb.set('a', 1, 'temp');
65
+ bb.set('b', 2, 'temp');
66
+ bb.set('c', 3, 'keep');
67
+ const removed = bb.clearScope('temp');
68
+ expect(removed).toBe(2);
69
+ expect(bb.has('a')).toBe(false);
70
+ expect(bb.has('c')).toBe(true);
71
+ });
72
+
73
+ it('getScopes lists all scopes', () => {
74
+ bb.set('a', 1, 'x');
75
+ bb.set('b', 2, 'y');
76
+ const scopes = bb.getScopes();
77
+ expect(scopes).toContain('x');
78
+ expect(scopes).toContain('y');
79
+ });
80
+
81
+ // ---------------------------------------------------------------------------
82
+ // Observers
83
+ // ---------------------------------------------------------------------------
84
+
85
+ it('observe fires callback on set', () => {
86
+ const cb = vi.fn();
87
+ bb.observe('hp', cb);
88
+ bb.set('hp', 100);
89
+ expect(cb).toHaveBeenCalledWith('hp', 100, undefined);
90
+ });
91
+
92
+ it('observe callback receives old value on update', () => {
93
+ const cb = vi.fn();
94
+ bb.set('hp', 100);
95
+ bb.observe('hp', cb);
96
+ bb.set('hp', 50);
97
+ expect(cb).toHaveBeenCalledWith('hp', 50, 100);
98
+ });
99
+
100
+ it('observeAll fires for any key change', () => {
101
+ const cb = vi.fn();
102
+ bb.observeAll(cb);
103
+ bb.set('a', 1);
104
+ bb.set('b', 2);
105
+ expect(cb).toHaveBeenCalledTimes(2);
106
+ });
107
+
108
+ // ---------------------------------------------------------------------------
109
+ // Version Tracking
110
+ // ---------------------------------------------------------------------------
111
+
112
+ it('getVersion increments on update', () => {
113
+ bb.set('hp', 100);
114
+ expect(bb.getVersion('hp')).toBe(0);
115
+ bb.set('hp', 50);
116
+ expect(bb.getVersion('hp')).toBe(1);
117
+ });
118
+
119
+ it('getVersion returns -1 for missing key', () => {
120
+ expect(bb.getVersion('nope')).toBe(-1);
121
+ });
122
+
123
+ // ---------------------------------------------------------------------------
124
+ // History
125
+ // ---------------------------------------------------------------------------
126
+
127
+ it('getHistory records changes', () => {
128
+ bb.set('hp', 100);
129
+ bb.set('hp', 50);
130
+ const history = bb.getHistory();
131
+ expect(history.length).toBe(2);
132
+ expect(history[0].key).toBe('hp');
133
+ expect(history[0].value).toBe(100);
134
+ expect(history[1].value).toBe(50);
135
+ });
136
+
137
+ // ---------------------------------------------------------------------------
138
+ // Queries
139
+ // ---------------------------------------------------------------------------
140
+
141
+ it('getEntryCount counts total entries', () => {
142
+ expect(bb.getEntryCount()).toBe(0);
143
+ bb.set('a', 1);
144
+ bb.set('b', 2);
145
+ expect(bb.getEntryCount()).toBe(2);
146
+ });
147
+
148
+ it('getKeys lists all keys', () => {
149
+ bb.set('x', 1);
150
+ bb.set('y', 2);
151
+ expect(bb.getKeys().sort()).toEqual(['x', 'y']);
152
+ });
153
+
154
+ // ---------------------------------------------------------------------------
155
+ // Clear
156
+ // ---------------------------------------------------------------------------
157
+
158
+ it('clear removes all data', () => {
159
+ bb.set('a', 1);
160
+ bb.set('b', 2);
161
+ bb.clear();
162
+ expect(bb.getEntryCount()).toBe(0);
163
+ expect(bb.getHistory()).toHaveLength(0);
164
+ });
165
+
166
+ // ---------------------------------------------------------------------------
167
+ // Serialization
168
+ // ---------------------------------------------------------------------------
169
+
170
+ it('toJSON exports key-value pairs', () => {
171
+ bb.set('hp', 100);
172
+ bb.set('name', 'hero');
173
+ const json = bb.toJSON();
174
+ expect(json.hp).toBe(100);
175
+ expect(json.name).toBe('hero');
176
+ });
177
+
178
+ it('fromJSON imports key-value pairs', () => {
179
+ bb.fromJSON({ x: 10, y: 20 });
180
+ expect(bb.get('x')).toBe(10);
181
+ expect(bb.get('y')).toBe(20);
182
+ });
183
+ });
@@ -0,0 +1,141 @@
1
+ /**
2
+ * GenerationAnalytics — Production Test Suite
3
+ *
4
+ * Covers: metric recording, aggregate computation, adapter metrics,
5
+ * confidence/response-time/error distributions, recommendations,
6
+ * time series, export, report generation, clear.
7
+ */
8
+ import { describe, it, expect } from 'vitest';
9
+ import {
10
+ GenerationAnalytics,
11
+ createAnalytics,
12
+ type GenerationMetrics,
13
+ } from '../GenerationAnalytics';
14
+
15
+ function metric(overrides: Partial<GenerationMetrics> = {}): GenerationMetrics {
16
+ return {
17
+ promptLength: overrides.promptLength ?? 100,
18
+ codeLength: overrides.codeLength ?? 200,
19
+ confidence: overrides.confidence ?? 0.8,
20
+ parseSuccess: overrides.parseSuccess ?? true,
21
+ errorCount: overrides.errorCount ?? 0,
22
+ wasFixed: overrides.wasFixed ?? false,
23
+ responseTimeMs: overrides.responseTimeMs ?? 500,
24
+ attemptsNeeded: overrides.attemptsNeeded ?? 1,
25
+ adapterName: overrides.adapterName ?? 'openai',
26
+ timestamp: overrides.timestamp ?? new Date(),
27
+ platform: overrides.platform ?? 'web',
28
+ };
29
+ }
30
+
31
+ describe('GenerationAnalytics — Production', () => {
32
+ // ─── Factory ──────────────────────────────────────────────────────
33
+ it('createAnalytics returns instance', () => {
34
+ expect(createAnalytics()).toBeInstanceOf(GenerationAnalytics);
35
+ });
36
+
37
+ // ─── Record + Get ─────────────────────────────────────────────────
38
+ it('recordMetric stores metric', () => {
39
+ const a = new GenerationAnalytics();
40
+ a.recordMetric(metric());
41
+ expect(a.getAllMetrics().length).toBe(1);
42
+ });
43
+
44
+ it('clearMetrics empties all', () => {
45
+ const a = new GenerationAnalytics();
46
+ a.recordMetric(metric());
47
+ a.clearMetrics();
48
+ expect(a.getAllMetrics().length).toBe(0);
49
+ });
50
+
51
+ // ─── Aggregates ───────────────────────────────────────────────────
52
+ it('getAggregateMetrics computes totals', () => {
53
+ const a = new GenerationAnalytics();
54
+ a.recordMetric(metric({ parseSuccess: true, confidence: 0.9, responseTimeMs: 100 }));
55
+ a.recordMetric(metric({ parseSuccess: false, confidence: 0.5, responseTimeMs: 300 }));
56
+ const agg = a.getAggregateMetrics();
57
+ expect(agg.totalGenerations).toBe(2);
58
+ expect(agg.successRate).toBe(0.5);
59
+ expect(agg.avgConfidence).toBeCloseTo(0.7, 1);
60
+ expect(agg.avgResponseTime).toBeCloseTo(200, 0);
61
+ });
62
+
63
+ // ─── Adapter Metrics ──────────────────────────────────────────────
64
+ it('getMetricsByAdapter returns per-adapter stats', () => {
65
+ const a = new GenerationAnalytics();
66
+ a.recordMetric(metric({ adapterName: 'openai', parseSuccess: true }));
67
+ a.recordMetric(metric({ adapterName: 'openai', parseSuccess: true }));
68
+ a.recordMetric(metric({ adapterName: 'claude', parseSuccess: false }));
69
+ const oai = a.getMetricsByAdapter('openai');
70
+ expect(oai.generationCount).toBe(2);
71
+ expect(oai.successRate).toBe(1);
72
+ });
73
+
74
+ it('getAllAdapterMetrics covers all adapters', () => {
75
+ const a = new GenerationAnalytics();
76
+ a.recordMetric(metric({ adapterName: 'openai' }));
77
+ a.recordMetric(metric({ adapterName: 'claude' }));
78
+ const all = a.getAllAdapterMetrics();
79
+ expect(all.length).toBe(2);
80
+ });
81
+
82
+ // ─── Distributions ────────────────────────────────────────────────
83
+ it('getConfidenceDistribution returns buckets', () => {
84
+ const a = new GenerationAnalytics();
85
+ a.recordMetric(metric({ confidence: 0.15 }));
86
+ a.recordMetric(metric({ confidence: 0.55 }));
87
+ a.recordMetric(metric({ confidence: 0.95 }));
88
+ const dist = a.getConfidenceDistribution();
89
+ expect(dist.length).toBeGreaterThan(0);
90
+ expect(dist.reduce((s, d) => s + d.count, 0)).toBe(3);
91
+ });
92
+
93
+ it('getResponseTimeDistribution returns buckets', () => {
94
+ const a = new GenerationAnalytics();
95
+ a.recordMetric(metric({ responseTimeMs: 50 }));
96
+ a.recordMetric(metric({ responseTimeMs: 500 }));
97
+ a.recordMetric(metric({ responseTimeMs: 5000 }));
98
+ const dist = a.getResponseTimeDistribution();
99
+ expect(dist.length).toBeGreaterThan(0);
100
+ });
101
+
102
+ it('getErrorPatterns categorizes errors', () => {
103
+ const a = new GenerationAnalytics();
104
+ a.recordMetric(metric({ parseSuccess: false, errorCount: 2 }));
105
+ a.recordMetric(metric({ parseSuccess: true, errorCount: 0 }));
106
+ const patterns = a.getErrorPatterns();
107
+ expect(patterns.length).toBeGreaterThan(0);
108
+ });
109
+
110
+ // ─── Recommendations ─────────────────────────────────────────────
111
+ it('getRecommendations returns array of strings', () => {
112
+ const a = new GenerationAnalytics();
113
+ a.recordMetric(metric());
114
+ const recs = a.getRecommendations();
115
+ expect(Array.isArray(recs)).toBe(true);
116
+ });
117
+
118
+ // ─── Export / Report ──────────────────────────────────────────────
119
+ it('exportMetrics returns valid JSON', () => {
120
+ const a = new GenerationAnalytics();
121
+ a.recordMetric(metric());
122
+ const json = a.exportMetrics();
123
+ expect(() => JSON.parse(json)).not.toThrow();
124
+ });
125
+
126
+ it('generateReport returns non-empty string', () => {
127
+ const a = new GenerationAnalytics();
128
+ a.recordMetric(metric());
129
+ const report = a.generateReport();
130
+ expect(report.length).toBeGreaterThan(0);
131
+ });
132
+
133
+ // ─── Time Series ──────────────────────────────────────────────────
134
+ it('getTimeSeries returns metrics windows', () => {
135
+ const a = new GenerationAnalytics();
136
+ const now = new Date();
137
+ a.recordMetric(metric({ timestamp: now }));
138
+ const ts = a.getTimeSeries(3600000);
139
+ expect(ts.length).toBeGreaterThanOrEqual(0); // may or may not window
140
+ });
141
+ });
@@ -0,0 +1,165 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { GenerationAnalytics } from '../GenerationAnalytics';
3
+ import type { GenerationMetrics } from '../GenerationAnalytics';
4
+
5
+ function makeMetric(overrides: Partial<GenerationMetrics> = {}): GenerationMetrics {
6
+ return {
7
+ promptLength: 50,
8
+ codeLength: 200,
9
+ confidence: 0.9,
10
+ parseSuccess: true,
11
+ errorCount: 0,
12
+ wasFixed: false,
13
+ responseTimeMs: 500,
14
+ attemptsNeeded: 1,
15
+ adapterName: 'gpt-4',
16
+ timestamp: new Date(),
17
+ ...overrides,
18
+ };
19
+ }
20
+
21
+ describe('GenerationAnalytics', () => {
22
+ let ga: GenerationAnalytics;
23
+
24
+ beforeEach(() => {
25
+ ga = new GenerationAnalytics();
26
+ });
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Record / Get
30
+ // ---------------------------------------------------------------------------
31
+
32
+ it('recordMetric stores a metric', () => {
33
+ ga.recordMetric(makeMetric());
34
+ expect(ga.getAllMetrics()).toHaveLength(1);
35
+ });
36
+
37
+ it('getAllMetrics returns all recorded metrics', () => {
38
+ ga.recordMetric(makeMetric());
39
+ ga.recordMetric(makeMetric({ confidence: 0.5 }));
40
+ expect(ga.getAllMetrics()).toHaveLength(2);
41
+ });
42
+
43
+ it('clearMetrics removes all metrics', () => {
44
+ ga.recordMetric(makeMetric());
45
+ ga.clearMetrics();
46
+ expect(ga.getAllMetrics()).toHaveLength(0);
47
+ });
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // Aggregate Metrics
51
+ // ---------------------------------------------------------------------------
52
+
53
+ it('getAggregateMetrics calculates totalGenerations', () => {
54
+ ga.recordMetric(makeMetric());
55
+ ga.recordMetric(makeMetric());
56
+ const agg = ga.getAggregateMetrics();
57
+ expect(agg.totalGenerations).toBe(2);
58
+ });
59
+
60
+ it('getAggregateMetrics computes success rate', () => {
61
+ ga.recordMetric(makeMetric({ parseSuccess: true }));
62
+ ga.recordMetric(makeMetric({ parseSuccess: false }));
63
+ const agg = ga.getAggregateMetrics();
64
+ expect(agg.successRate).toBeCloseTo(0.5);
65
+ });
66
+
67
+ it('getAggregateMetrics computes average confidence', () => {
68
+ ga.recordMetric(makeMetric({ confidence: 0.8 }));
69
+ ga.recordMetric(makeMetric({ confidence: 0.6 }));
70
+ const agg = ga.getAggregateMetrics();
71
+ expect(agg.avgConfidence).toBeCloseTo(0.7);
72
+ });
73
+
74
+ it('getAggregateMetrics computes average response time', () => {
75
+ ga.recordMetric(makeMetric({ responseTimeMs: 400 }));
76
+ ga.recordMetric(makeMetric({ responseTimeMs: 600 }));
77
+ const agg = ga.getAggregateMetrics();
78
+ expect(agg.avgResponseTime).toBeCloseTo(500);
79
+ });
80
+
81
+ // ---------------------------------------------------------------------------
82
+ // Adapter Metrics
83
+ // ---------------------------------------------------------------------------
84
+
85
+ it('getMetricsByAdapter returns per-adapter stats', () => {
86
+ ga.recordMetric(makeMetric({ adapterName: 'gpt-4', confidence: 0.9 }));
87
+ ga.recordMetric(makeMetric({ adapterName: 'gpt-4', confidence: 0.8 }));
88
+ ga.recordMetric(makeMetric({ adapterName: 'claude', confidence: 0.7 }));
89
+ const gpt = ga.getMetricsByAdapter('gpt-4');
90
+ expect(gpt.generationCount).toBe(2);
91
+ expect(gpt.avgConfidence).toBeCloseTo(0.85);
92
+ });
93
+
94
+ it('getAllAdapterMetrics lists all adapters', () => {
95
+ ga.recordMetric(makeMetric({ adapterName: 'a' }));
96
+ ga.recordMetric(makeMetric({ adapterName: 'b' }));
97
+ const all = ga.getAllAdapterMetrics();
98
+ expect(all.length).toBe(2);
99
+ });
100
+
101
+ // ---------------------------------------------------------------------------
102
+ // Confidence Distribution
103
+ // ---------------------------------------------------------------------------
104
+
105
+ it('getConfidenceDistribution returns buckets', () => {
106
+ ga.recordMetric(makeMetric({ confidence: 0.15 }));
107
+ ga.recordMetric(makeMetric({ confidence: 0.85 }));
108
+ const dist = ga.getConfidenceDistribution();
109
+ expect(dist.length).toBeGreaterThan(0);
110
+ // Should have at least entries in low and high buckets
111
+ const total = dist.reduce((sum, d) => sum + d.count, 0);
112
+ expect(total).toBe(2);
113
+ });
114
+
115
+ // ---------------------------------------------------------------------------
116
+ // Error Patterns
117
+ // ---------------------------------------------------------------------------
118
+
119
+ it('getErrorPatterns identifies error types', () => {
120
+ ga.recordMetric(makeMetric({ parseSuccess: false, errorCount: 2 }));
121
+ ga.recordMetric(makeMetric({ parseSuccess: true, errorCount: 0 }));
122
+ const patterns = ga.getErrorPatterns();
123
+ expect(patterns).toBeDefined();
124
+ });
125
+
126
+ // ---------------------------------------------------------------------------
127
+ // Recommendations
128
+ // ---------------------------------------------------------------------------
129
+
130
+ it('getRecommendations returns suggestions array', () => {
131
+ ga.recordMetric(makeMetric({ confidence: 0.3, parseSuccess: false }));
132
+ ga.recordMetric(makeMetric({ confidence: 0.2, parseSuccess: false }));
133
+ const recs = ga.getRecommendations();
134
+ expect(Array.isArray(recs)).toBe(true);
135
+ });
136
+
137
+ // ---------------------------------------------------------------------------
138
+ // Export / Report
139
+ // ---------------------------------------------------------------------------
140
+
141
+ it('exportMetrics returns JSON string', () => {
142
+ ga.recordMetric(makeMetric());
143
+ const json = ga.exportMetrics();
144
+ const parsed = JSON.parse(json);
145
+ expect(parsed).toBeDefined();
146
+ });
147
+
148
+ it('generateReport returns readable string', () => {
149
+ ga.recordMetric(makeMetric());
150
+ const report = ga.generateReport();
151
+ expect(typeof report).toBe('string');
152
+ expect(report.length).toBeGreaterThan(0);
153
+ });
154
+
155
+ // ---------------------------------------------------------------------------
156
+ // Response Time Distribution
157
+ // ---------------------------------------------------------------------------
158
+
159
+ it('getResponseTimeDistribution returns buckets', () => {
160
+ ga.recordMetric(makeMetric({ responseTimeMs: 100 }));
161
+ ga.recordMetric(makeMetric({ responseTimeMs: 5000 }));
162
+ const dist = ga.getResponseTimeDistribution();
163
+ expect(dist.length).toBeGreaterThan(0);
164
+ });
165
+ });
@@ -0,0 +1,144 @@
1
+ /**
2
+ * GenerationCache (LRU) — Production Test Suite
3
+ *
4
+ * Covers: set/get, key generation, TTL expiry, LRU eviction,
5
+ * statistics, serialization, entries, remove, getSize, clear.
6
+ *
7
+ * Note: GenerationCache uses node's crypto module for hashing,
8
+ * which Vitest supports natively.
9
+ */
10
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
11
+ import { GenerationCache } from '../GenerationCache';
12
+
13
+ describe('GenerationCache — Production', () => {
14
+ // Helper function to mock Date.now with offset or counter
15
+ const mockDateNow = (offsetMs: number) => {
16
+ const originalNow = Date.now;
17
+ Date.now = vi.fn(() => originalNow() + offsetMs);
18
+ return () => {
19
+ Date.now = originalNow;
20
+ };
21
+ };
22
+
23
+ const mockDateNowIncrementing = (startOffset: number = 0) => {
24
+ const originalNow = Date.now;
25
+ let counter = startOffset;
26
+ Date.now = vi.fn(() => originalNow() + counter++);
27
+ return () => {
28
+ Date.now = originalNow;
29
+ };
30
+ };
31
+
32
+ // ─── Basic Get/Set ────────────────────────────────────────────────
33
+ it('set + get returns cached entry', () => {
34
+ const cache = new GenerationCache();
35
+ cache.set('prompt1', 'code1', 0.9, 'openai');
36
+ const entry = cache.get('prompt1', 'openai');
37
+ expect(entry).not.toBeNull();
38
+ expect(entry!.code).toBe('code1');
39
+ expect(entry!.confidence).toBe(0.9);
40
+ });
41
+
42
+ it('get returns null on miss', () => {
43
+ const cache = new GenerationCache();
44
+ expect(cache.get('unknown', 'openai')).toBeNull();
45
+ });
46
+
47
+ it('different adapters have different keys', () => {
48
+ const cache = new GenerationCache();
49
+ cache.set('prompt', 'code-oai', 0.9, 'openai');
50
+ cache.set('prompt', 'code-cl', 0.8, 'claude');
51
+ expect(cache.get('prompt', 'openai')!.code).toBe('code-oai');
52
+ expect(cache.get('prompt', 'claude')!.code).toBe('code-cl');
53
+ });
54
+
55
+ // ─── Hit Tracking ─────────────────────────────────────────────────
56
+ it('get increments hit count', () => {
57
+ const cache = new GenerationCache();
58
+ cache.set('p', 'c', 0.9, 'a');
59
+ cache.get('p', 'a');
60
+ cache.get('p', 'a');
61
+ const entry = cache.get('p', 'a');
62
+ expect(entry!.hits).toBe(3);
63
+ });
64
+
65
+ // ─── TTL Expiry ───────────────────────────────────────────────────
66
+ it('expired entries are not returned', () => {
67
+ const cache = new GenerationCache({ ttlMs: 100 });
68
+ cache.set('p', 'c', 0.9, 'a');
69
+
70
+ // Fast-forward time
71
+ const restoreDateNow = mockDateNow(200);
72
+ expect(cache.get('p', 'a')).toBeNull();
73
+ restoreDateNow();
74
+ });
75
+
76
+ // ─── LRU Eviction ─────────────────────────────────────────────────
77
+ it('evicts oldest when maxSize reached', () => {
78
+ const cache = new GenerationCache({ maxSize: 2 });
79
+ cache.set('first', 'c1', 0.9, 'a');
80
+
81
+ // Ensure "second" and "third" are newer with incrementing timestamps
82
+ const restoreDateNow = mockDateNowIncrementing(1);
83
+
84
+ cache.set('second', 'c2', 0.9, 'a');
85
+ cache.set('third', 'c3', 0.9, 'a'); // should evict 'first'
86
+
87
+ restoreDateNow();
88
+
89
+ expect(cache.get('first', 'a')).toBeNull();
90
+ expect(cache.get('third', 'a')).not.toBeNull();
91
+ });
92
+
93
+ // ─── Statistics ───────────────────────────────────────────────────
94
+ it('getStats tracks hits and misses', () => {
95
+ const cache = new GenerationCache();
96
+ cache.set('p', 'c', 0.9, 'a');
97
+ cache.get('p', 'a'); // hit
98
+ cache.get('x', 'a'); // miss
99
+ const stats = cache.getStats();
100
+ expect(stats.totalHits).toBe(1);
101
+ expect(stats.totalMisses).toBe(1);
102
+ expect(stats.hitRate).toBeCloseTo(0.5);
103
+ expect(stats.entriesCount).toBe(1);
104
+ });
105
+
106
+ // ─── Serialization ───────────────────────────────────────────────
107
+ it('serialize produces valid JSON', () => {
108
+ const cache = new GenerationCache();
109
+ cache.set('p', 'c', 0.9, 'a');
110
+ const json = cache.serialize();
111
+ expect(() => JSON.parse(json)).not.toThrow();
112
+ });
113
+
114
+ // ─── Entries / Remove / Size ──────────────────────────────────────
115
+ it('getEntries returns all cached entries', () => {
116
+ const cache = new GenerationCache();
117
+ cache.set('a', 'ca', 0.9, 'openai');
118
+ cache.set('b', 'cb', 0.8, 'openai');
119
+ expect(cache.getEntries().length).toBe(2);
120
+ });
121
+
122
+ it('remove deletes specific entry', () => {
123
+ const cache = new GenerationCache();
124
+ cache.set('p', 'c', 0.9, 'a');
125
+ expect(cache.remove('p', 'a')).toBe(true);
126
+ expect(cache.get('p', 'a')).toBeNull();
127
+ });
128
+
129
+ it('getSize returns approximate byte size', () => {
130
+ const cache = new GenerationCache();
131
+ cache.set('p', 'c', 0.9, 'a');
132
+ expect(cache.getSize()).toBeGreaterThan(0);
133
+ });
134
+
135
+ // ─── Clear ────────────────────────────────────────────────────────
136
+ it('clear empties cache and resets stats', () => {
137
+ const cache = new GenerationCache();
138
+ cache.set('p', 'c', 0.9, 'a');
139
+ cache.get('p', 'a');
140
+ cache.clear();
141
+ expect(cache.getEntries().length).toBe(0);
142
+ expect(cache.getStats().totalHits).toBe(0);
143
+ });
144
+ });