Rystem.PlayFramework 10.0.6

dotnet add package Rystem.PlayFramework --version 10.0.6
                    
NuGet\Install-Package Rystem.PlayFramework -Version 10.0.6
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Rystem.PlayFramework" Version="10.0.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Rystem.PlayFramework" Version="10.0.6" />
                    
Directory.Packages.props
<PackageReference Include="Rystem.PlayFramework" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Rystem.PlayFramework --version 10.0.6
                    
#r "nuget: Rystem.PlayFramework, 10.0.6"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Rystem.PlayFramework@10.0.6
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Rystem.PlayFramework&version=10.0.6
                    
Install as a Cake Addin
#tool nuget:?package=Rystem.PlayFramework&version=10.0.6
                    
Install as a Cake Tool

What is Rystem?

Play Framework

Docs

Introduction

The Rystem.PlayFramework library enables developers to integrate multi-agent systems and OpenAI into their .NET applications. It provides tools to define and orchestrate complex agent-based scenarios using OpenAI's API. In this guide, we'll cover how to install, configure, and use the UseAiEndpoints middleware and PlayFramework scenes within a .NET application.

Prerequisites

To use this library, you need:

  • .NET 9.0 SDK or later
  • An OpenAI API key and endpoint
  • Access to an HTTP API (if you're using scenes with HTTP clients)
  • A database or service to handle identity management (optional)

Installation

Follow these steps to set up Rystem.PlayFramework:

  1. Clone or download the repository from GitHub.
  2. Add the package to your project using the NuGet package manager:
    dotnet add package Rystem.PlayFramework
    

Project Setup

The .csproj file is already configured for building the package, including symbol generation and source embedding for debugging purposes. The most important parts are:

  • TargetFramework: net9.0
  • Package Information: Contains metadata like authorship, versioning, repository URL, and licensing.

Ensure that your csproj file contains the necessary framework and package references.

Configuration

In your .NET application, you will need to configure the services and middlewares for Rystem.PlayFramework. This is done primarily within two extension methods:

  1. AddServices(IServiceCollection services, IConfiguration configuration): This method sets up the necessary services, such as OpenAI configuration, HTTP client, identity management, and PlayFramework scenes.

    Example setup:

    //setup OpenAi client
    services.AddOpenAi(x =>
     {
         x.ApiKey = configuration["OpenAi2:ApiKey"]!;
         x.Azure.ResourceName = configuration["OpenAi2:ResourceName"]!;
         x.Version = "2024-08-01-preview";
         x.DefaultRequestConfiguration.Chat = chatClient =>
         {
             chatClient.WithModel(configuration["OpenAi2:ModelName"]!);
         };
         x.PriceBuilder
         .AddModel(ChatModelName.Gpt4_o,
         new OpenAiCost { Units = 0.0000025m, Kind = KindOfCost.Input, UnitOfMeasure = UnitOfMeasure.Tokens },
         new OpenAiCost { Kind = KindOfCost.CachedInput, UnitOfMeasure = UnitOfMeasure.Tokens, Units = 0.00000125m },
         new OpenAiCost { Kind = KindOfCost.Output, UnitOfMeasure = UnitOfMeasure.Tokens, Units = 0.00001m });
     }, "playframework");
     //setup http client to use during play framework integration to call external services
     services.AddHttpClient("apiDomain", x =>
     {
         x.BaseAddress = new Uri(configuration["Api:Uri"]!);
     });
     //setup for Play Framework
     services.AddPlayFramework(scenes =>
     {
         scenes.Configure(settings =>
         {
             settings.OpenAi.Name = "playframework";
         })
         .AddMainActor((context) => $"Oggi � {DateTime.UtcNow}.", true)
         .AddScene(scene =>
         {
             scene
                 .WithName("Weather")
                 .WithDescription("Get information about the weather")
                 .WithHttpClient("apiDomain")
                 .WithOpenAi("playframework")
                 .WithApi(pathBuilder =>
                 {
                     pathBuilder
                         .Map(new Regex("Country/*"))
                         .Map(new Regex("City/*"))
                         .Map("Weather/");
                 })
                     .WithActors(actors =>
                     {
                         actors
                             .AddActor("Nel caso non esistesse la citt� richiesta potresti aggiungerla con il numero dei suoi abitanti.")
                             .AddActor("Ricordati che va sempre aggiunta anche la nazione, quindi se non c'� la nazione aggiungi anche quella.")
                             .AddActor("Non chiamare alcun meteo prima di assicurarti che tutto sia stato popolato correttamente.")
                             .AddActor<ActorWithDbRequest>();
                     });
         })
         .AddScene(scene =>
         {
             scene
             .WithName("Identity")
             .WithDescription("Get information about the user")
             .WithOpenAi("openai")
             .WithService<IdentityManager>(builder =>
             {
                 builder.WithMethod(x => x.GetNameAsync);
             });
         });
     });
    
  2. UseMiddlewares(IApplicationBuilder app): This method enables middleware components for routing, authorization, HTTPS redirection, and AI endpoints. It also maps OpenAPI and Scalar API routes.

    Example middleware setup:

    app.UseRouting();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapOpenApi();
        endpoints.MapScalarApiReference();
        endpoints.MapControllers();
    });
    app.UseHttpsRedirection();
    app.UseAuthorization();
    app.UseAiEndpoints(); // Enables AI endpoints for your application
    

Configuration Options

  • OpenAI Configuration: The OpenAI settings are fetched from the appsettings.json file using configuration keys such as OpenAi:ApiKey, OpenAi:Endpoint, and OpenAi:ModelName.
  • HTTP Client: A named HTTP client (apiDomain) is used for interacting with external APIs.
  • Scenes: You can define multiple scenes within PlayFramework, each tied to specific functionality (e.g., Weather and Identity in this example).

Example appsettings.json

{
  "OpenAi": {
    "ApiKey": "your-openai-api-key",
    "Endpoint": "https://api.openai.com",
    "ModelName": "gpt-4"
  },
  "Api": {
    "Uri": "https://api.example.com/"
  }
}

Usage

Defining a Scene

A scene in PlayFramework represents a unit of interaction. Each scene can have:

  • Name: A descriptive name of the scene.
  • Description: A short description of the scene's purpose.
  • HttpClient: The HTTP client to use when making API requests within the scene.
  • Actors: Define behaviors or actions that should be executed as part of the scene.
Example Scene: Weather
scene.WithName("Weather")
    .WithDescription("Get information about the weather")
    .WithHttpClient("apiDomain")
    .WithOpenAi("openai")
    .WithApi(pathBuilder =>
    {
        pathBuilder.Map(new Regex("Country/*"))
            .Map(new Regex("City/*"))
            .Map("Weather/");
    })
    .WithActors(actors =>
    {
        actors.AddActor("Ensure that the requested city exists, or add it with its population.")
              .AddActor<ActorWithDbRequest>(); 
    });

Example Scene: Identity Management

The IdentityManager service is used to manage user identities. It can be configured and used as follows:

scene.WithName("Identity")
    .WithDescription("Get information about the user")
    .WithOpenAi("openai")
    .WithService<IdentityManager>(builder =>
    {
        builder.WithMethod(x => x.GetNameAsync);
    });

This setup allows you to fetch information about a user, possibly using OpenAI in the process.

Middleware

The UseAiEndpoints() middleware is essential for enabling AI-powered interactions in your API. Once registered, it allows your application to respond to AI-driven requests via OpenAI or custom scenes.

How AI Endpoints Work

Endpoint Definition

The primary endpoint that gets mapped by UseAiEndpoints is api/ai/message. This endpoint receives a query string parameter m (which represents the user's message or request) and uses the ISceneManager to handle the message. The AI service or scene logic processes the message and returns a result.

Example of a GET Request:
GET https://yourdomain.com/api/ai/message?m=What%20is%20the%20weather%20today?

This will trigger the scene manager to process the message (e.g., fetching weather information using an AI service or scene logic).

Endpoint Logic

  • Input: The endpoint expects a query string parameter m (message), which will be processed by the scene manager.
  • Service Dependency: The ISceneManager service is injected and handles the business logic of processing the message.
  • Response: The result of the scene or AI interaction is returned as a response to the client.

Authorization

The endpoints can be configured to require authorization either by default (using the isAuthorized parameter) or by specifying custom authorization policies.

Example of enforcing authorization:

app.UseAiEndpoints("AdminPolicy", "UserPolicy"); // Only users matching these policies can access AI endpoints

AI Endpoint Response (JSON Format)

When a request is made to the AI endpoint (api/ai/message), the response is returned in JSON format. The structure of the response is defined by the AiSceneResponse class.

JSON Response Structure

The JSON response will contain the following fields:

  • Id: A unique identifier for the request, generated as a GUID.
  • Name: The name of the scene that handled the request (optional).
  • FunctionName: The name of the specific function or action executed by the scene (optional).
  • Message: The original message or query that was sent to the AI endpoint.
  • Arguments: Any arguments that were passed to the function (optional).
  • Response: The result or output generated by the AI or scene.

Example JSON Response

{
  "Id": "123e4567-e89b-12d3-a456-426614174000",
  "Name": "Weather",
  "FunctionName": "GetWeatherInfo",
  "Message": "What is the weather in New York?",
  "Arguments": "City: New York, Country: USA",
  "Response": "The weather in New York is sunny with a temperature of 25�C."
}

Field Descriptions

  • Id: A unique request identifier.
  • Name: Name of the scene that handled the request, e.g., "Weather".
  • FunctionName: The name of the specific function invoked by the scene, e.g., "GetWeatherInfo".
  • Message: The original query made by the user, e.g., "What is the weather in New York?".
  • Arguments: Any relevant parameters that were processed as part of the function, e.g., "City: New York, Country: USA".
  • Response: The result of the AI's or scene's execution, e.g., "The weather in New York is sunny with a temperature of 25�C.".

This structured response allows clients to interpret and use the output of the AI or multi-agent system in a consistent manner.

MCP Server Integration

PlayFramework now supports integration with Model Context Protocol (MCP) servers, enabling your scenes to access tools, resources, and prompts exposed by MCP servers. This allows for seamless integration with external services and capabilities.

What is MCP?

The Model Context Protocol (MCP) is a standardized protocol for exposing capabilities to AI models. MCP servers can expose:

  • Tools: Functions that the AI can call to perform actions
  • Resources: Static or dynamic data that the AI can access and read
  • Prompts: Pre-configured prompts or templates that provide context to the AI

Setting Up an MCP Server

MCP servers can be registered globally during application startup and then selectively used by individual scenes.

1. Register an MCP Server

In your service configuration, use the AddMcpServer method to register an MCP server:

services.AddPlayFramework(scenes =>
{
    scenes.Configure(settings =>
    {
        settings.OpenAi.Name = "playframework";
    })
    .AddMcpServer("myMcpServer", mcp =>
    {
        // Configure HTTP-based MCP server
        mcp.WithHttpServer("http://localhost:3000");
        // Optionally set custom timeout (default is 30 seconds)
        mcp.WithTimeout(TimeSpan.FromSeconds(60));
    })
    .AddScene(scene =>
    {
        // Configure scene to use MCP server tools
        scene.UseMcpServer("myMcpServer");
    });
});
2. Configuring MCP Elements in a Scene

Each scene can independently decide which MCP elements to use via the UseMcpServer() method:

.AddScene(scene =>
{
    scene
        .WithName("DataProcessing")
        .WithDescription("Process data using MCP tools")
        .WithOpenAi("playframework")
        // Use all MCP elements (tools, resources, prompts)
        .UseMcpServer("myMcpServer")
        // Or configure with filters
        .UseMcpServer("myMcpServer", filterBuilder =>
        {
            filterBuilder.WithTools(toolConfig =>
            {
                toolConfig.Whitelist("get_data", "process_*");
            });
        });
})

Filtering MCP Elements

You can fine-tune which MCP elements are available in each scene using fluent builder methods:

Using All Elements
// Enable tools, resources, and prompts
.UseMcpServer("myMcpServer")
Using Only Specific Element Types
// Use only tools, disable resources and prompts
.UseMcpServer("myMcpServer", filterBuilder =>
{
    filterBuilder.OnlyTools();
})

// Use only resources, disable tools and prompts
.UseMcpServer("myMcpServer", filterBuilder =>
{
    filterBuilder.OnlyResources();
})

// Use only prompts, disable tools and resources
.UseMcpServer("myMcpServer", filterBuilder =>
{
    filterBuilder.OnlyPrompts();
})
Filtering by Name

Each element type supports filtering by name patterns:

.UseMcpServer("myMcpServer", filterBuilder =>
{
    filterBuilder.WithTools(toolConfig =>
    {
        // Whitelist specific tools
        toolConfig.Whitelist("get_user", "get_profile");
        
        // Or use patterns
        toolConfig.Whitelist("get_*", "list_*");
        
        // Or match by regex
        toolConfig.Regex("^(get|list)_.*");
        
        // Or use startsWith
        toolConfig.StartsWith("process_");
        
        // Or use a custom predicate
        toolConfig.Predicate(toolName => !toolName.StartsWith("admin_"));
        
        // Or exclude specific tools
        toolConfig.Exclude("dangerous_operation");
    });
})

The same filtering options are available for resources and prompts:

.UseMcpServer("myMcpServer", filterBuilder =>
{
    filterBuilder
        .WithTools(toolConfig => toolConfig.Whitelist("get_*"))
        .WithResources(resourceConfig => resourceConfig.Whitelist("data_*"))
        .WithPrompts(promptConfig => promptConfig.Exclude("internal_*"));
})

Complete Example: Multi-Service Scene with MCP

services.AddPlayFramework(scenes =>
{
    scenes.Configure(settings =>
    {
        settings.OpenAi.Name = "playframework";
    })
    // Register MCP server
    .AddMcpServer("dataServer", mcp =>
    {
        mcp.WithHttpServer("http://localhost:3000");
    })
    // Add HTTP client for API calls
    .AddHttpClient("apiDomain", x =>
    {
        x.BaseAddress = new Uri("https://api.example.com/");
    })
    // Create a scene that uses both MCP tools and HTTP APIs
    .AddScene(scene =>
    {
        scene
            .WithName("DataProcessing")
            .WithDescription("Process and retrieve data using MCP and HTTP APIs")
            .WithOpenAi("playframework")
            .WithHttpClient("apiDomain")
            // Use MCP tools with filtering
            .UseMcpServer("dataServer", filterBuilder =>
            {
                filterBuilder.WithTools(toolConfig =>
                {
                    toolConfig.Whitelist("get_*", "process_*");
                });
            })
            // Register additional service methods
            .WithService<DataManager>(builder =>
            {
                builder.WithMethod(x => x.GetDataAsync);
            });
    });
});

MCP Server Types

PlayFramework supports different MCP server communication methods:

HTTP Server

Connect to an MCP server via HTTP:

.AddMcpServer("httpMcp", mcp =>
{
    mcp.WithHttpServer("http://localhost:3000");
})
Stdio Command

Connect to an MCP server via stdio (useful for local executables):

.AddMcpServer("localMcp", mcp =>
{
    mcp.WithCommand("node", "path/to/mcp/server.js");
    mcp.WithTimeout(TimeSpan.FromSeconds(30));
})

How MCP Elements Are Injected

  • Tools: Available as callable functions that the AI can invoke to perform actions
  • Resources: Injected as system messages with their content
  • Prompts: Injected as system messages providing context and guidance

The AI automatically learns what tools, resources, and prompts are available and uses them appropriately to solve the given task.

Exposing PlayFramework as an MCP Server

In addition to consuming MCP servers, PlayFramework can also expose itself as an MCP server, allowing external MCP clients (like Claude Desktop, other AI agents, or custom applications) to consume your PlayFramework as a tool.

Why Expose as MCP Server?

  • Interoperability: Allow any MCP-compatible client to use your PlayFramework
  • Claude Desktop Integration: Users can add your PlayFramework directly to Claude Desktop
  • Multi-Agent Scenarios: Other AI agents can call your PlayFramework as a tool
  • Standardized API: Uses the standard MCP JSON-RPC protocol

Configuration

1. Enable MCP Server Exposure

When configuring your PlayFramework, use ExposeAsMcpServer():

services.AddPlayFramework(builder =>
{
    // Expose this PlayFramework as an MCP server
    builder.ExposeAsMcpServer(config =>
    {
        config.Description = "AI Assistant for customer support and order management";
        config.Prompt = "You are a helpful assistant...";  // Optional
        config.EnableResources = true;  // Default: true - generates scene documentation
        config.AuthorizationPolicy = "ApiKeyPolicy";  // Optional - null = public access
    });

    // Add your scenes as usual
    builder.AddScene(scene =>
    {
        scene.WithName("CustomerSupport")
             .WithDescription("Handles customer inquiries and complaints");
    });

    builder.AddScene(scene =>
    {
        scene.WithName("OrderManagement")
             .WithDescription("Manages orders, returns, and shipping");
    });
}, name: "MyAssistant");
2. Map MCP Endpoints

In your Program.cs, map the MCP endpoints:

var app = builder.Build();

// Map MCP endpoints for all exposed PlayFrameworks
app.MapPlayFrameworkMcpEndpoints("/mcp");

app.Run();

This creates a JSON-RPC endpoint at:

  • POST /mcp/MyAssistant

Authorization

Authorization is configured at the PlayFramework level using standard .NET authorization policies:

// Configure authorization policy
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("ApiKeyPolicy", policy =>
        policy.RequireAssertion(ctx =>
        {
            var httpContext = ctx.Resource as HttpContext;
            return httpContext?.Request.Headers["X-Api-Key"] == "your-secret-key";
        }));
});

// Apply to PlayFramework
builder.ExposeAsMcpServer(config =>
{
    config.AuthorizationPolicy = "ApiKeyPolicy";
});

If AuthorizationPolicy is null or not set, the endpoint allows anonymous access.

Supported MCP Methods

The exposed MCP server supports all standard MCP methods:

Method Description
tools/list Returns the PlayFramework as a single tool
tools/call Executes the PlayFramework with the provided message
resources/list Returns documentation for each scene
resources/read Returns the markdown documentation content
prompts/list Returns configured prompts (if set)
prompts/get Returns the prompt content

Example: Using with Claude Desktop

Add to your Claude Desktop configuration (claude_desktop_config.json):

{
  "mcpServers": {
    "my-assistant": {
      "url": "http://localhost:5000/mcp/MyAssistant"
    }
  }
}

Example JSON-RPC Request/Response

Request (tools/call)
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "MyAssistant",
    "arguments": {
      "message": "What's the status of order #12345?"
    }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Order #12345 is currently being shipped and will arrive by Friday."
      }
    ],
    "isError": false
  }
}

Complete Example: Full MCP Integration

Here's an example that both consumes an external MCP server AND exposes the PlayFramework as an MCP server:

services.AddPlayFramework(builder =>
{
    // Configure settings
    builder.Configure(settings =>
    {
        settings.OpenAi.Name = "playframework";
    });

    // Consume external MCP server
    builder.AddMcpServer("externalTools", mcp =>
    {
        mcp.WithHttpServer("http://external-mcp-server:3000");
    });

    // Expose THIS PlayFramework as an MCP server
    builder.ExposeAsMcpServer(config =>
    {
        config.Description = "Multi-scene AI assistant with data processing";
        config.EnableResources = true;
    });

    // Scene that uses external MCP tools
    builder.AddScene(scene =>
    {
        scene.WithName("DataProcessing")
             .WithDescription("Processes data using external tools")
             .UseMcpServer("externalTools", filter =>
             {
                 filter.WithTools(t => t.Whitelist("process_*", "transform_*"));
             });
    });

}, name: "DataAssistant");

// In Program.cs
app.MapPlayFrameworkMcpEndpoints("/mcp");
// Now accessible at: POST /mcp/DataAssistant

Example of a weather scene

[
    {
        "id": "18b0628a-5202-47cc-813d-099702be3153",
        "name": "Weather",
        "functionName": null,
        "message": "Starting",
        "arguments": null,
        "response": null
    },
    {
        "id": "55f4754f-f372-40a2-a805-ce50c5d1c88d",
        "name": "Weather",
        "functionName": "Country_CityExists",
        "message": null,
        "arguments": "\"{\n  \u0022city\u0022: \u0022milan\u0022\n}\"",
        "response": "false"
    },
    {
        "id": "455e0384-a52e-4ee3-81d1-e0f9478a8484",
        "name": "Weather",
        "functionName": "Country_AddCity",
        "message": null,
        "arguments": "\"{\n  \u0022city\u0022: {\n    \u0022Name\u0022: \u0022milan\u0022,\n    \u0022Country\u0022: \u0022Italy\u0022,\n    \u0022Population\u0022: 1366180\n  }\n}\"",
        "response": "\"true\""
    },
    {
        "id": "aca48a7a-bb8d-4352-9d79-eb0535a2f6ed",
        "name": "Weather",
        "functionName": "Country_Exists",
        "message": null,
        "arguments": "\"{\n  \u0022country\u0022: \u0022Italia\u0022\n}\"",
        "response": "false"
    },
    {
        "id": "cdaf0ae2-3663-4287-94cf-edd1062e6bd8",
        "name": "Weather",
        "functionName": "Country_AddCountry",
        "message": null,
        "arguments": "\"{\n  \u0022country\u0022: {\n    \u0022Name\u0022: \u0022Italy\u0022,\n    \u0022Population\u0022: 60461826\n  }\n}\"",
        "response": "\"true\""
    },
    {
        "id": "0d369878-cc5f-4563-86a9-0714d909b85f",
        "name": "Weather",
        "functionName": "WeatherForecast_Get",
        "message": null,
        "arguments": "\"{\n  \u0022city\u0022: \u0022milan\u0022\n}\"",
        "response": "[{\"date\":\"2024-10-19\",\"temperatureC\":20,\"temperatureF\":67,\"summary\":\"Cool\"},{\"date\":\"2024-10-20\",\"temperatureC\":20,\"temperatureF\":67,\"summary\":\"Hot\"},{\"date\":\"2024-10-21\",\"temperatureC\":20,\"temperatureF\":67,\"summary\":\"Cool\"},{\"date\":\"2024-10-22\",\"temperatureC\":20,\"temperatureF\":67,\"summary\":\"Freezing\"},{\"date\":\"2024-10-23\",\"temperatureC\":20,\"temperatureF\":67,\"summary\":\"Hot\"}]"
    },
    {
        "id": "13192a15-e4c6-44d4-aec9-d38253c2dfe4",
        "name": "Weather",
        "functionName": null,
        "message": "Il tempo oggi a Milano � fresco con una temperatura di 20 gradi Celsius (67 gradi Fahrenheit).",
        "arguments": null,
        "response": null
    }
]

Building and Testing

To build the project, run:

dotnet build

To run tests, add a test project or directly execute requests against your API to validate the scene and actor configurations.

Conclusion

By following this guide, you can successfully install, configure, and use Rystem.PlayFramework with OpenAI integration. The key steps are setting up the services, defining scenes, and utilizing the AI middleware to handle interactions in your .NET application.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.0.6 35 1/23/2026
10.0.5 132 12/12/2025
10.0.4 138,167 12/1/2025
10.0.3 175 11/26/2025
10.0.2 191 11/25/2025
10.0.1 327 11/21/2025
10.0.0 291 11/13/2025
9.0.41 254 8/4/2025
9.0.40 147 7/31/2025
9.0.39 160 7/31/2025
9.0.38 488,864 7/28/2025
9.0.37 373,399 5/13/2025
9.0.36 309 5/12/2025
9.0.35 92,868 4/28/2025
9.0.34 44,474 4/20/2025
9.0.33 49,749 4/15/2025
9.0.32 256 4/15/2025
9.0.31 5,793 4/3/2025
9.0.30 88,860 3/24/2025
9.0.29 8,986 3/18/2025
9.0.28 206 3/17/2025
9.0.26 226 3/13/2025
9.0.25 52,123 3/9/2025
9.0.20 190 3/9/2025
9.0.19 287 3/7/2025
9.0.18 260 3/6/2025
9.0.17 19,529 3/6/2025
9.0.16 269 3/4/2025
9.0.15 302,535 1/16/2025
9.0.14 288 1/16/2025
9.0.13 126 1/13/2025
9.0.12 12,837 1/13/2025
9.0.11 120 1/13/2025
9.0.10 23,996 1/9/2025
9.0.9 119 1/9/2025
9.0.8 144 1/9/2025
9.0.7 3,986 1/8/2025
9.0.6 130 1/7/2025
9.0.5 12,512 1/6/2025
9.0.4 19,157 1/3/2025
9.0.3 73,343 12/22/2024
9.0.2 11,780 12/14/2024
9.0.1 68,696 12/10/2024
9.0.1-pre.4 102 12/9/2024
9.0.1-pre.3 101 12/8/2024
9.0.1-pre.2 107 12/7/2024
9.0.1-pre.1 99 12/7/2024
9.0.0 104,470 11/16/2024
9.0.0-rc.9 115 10/21/2024
9.0.0-rc.8 123 10/20/2024
9.0.0-rc.7 120 10/20/2024
9.0.0-rc.6 101 10/20/2024
9.0.0-rc.5 106 10/20/2024
9.0.0-rc.4 101 10/20/2024
9.0.0-rc.3 96 10/20/2024
9.0.0-rc.2 106 10/20/2024
9.0.0-rc.1 156 10/18/2024