Status
SteamPeaks
ChartsSalesUpcomingPatchesEventsCalculator
New on SteamEvery app, DLC and depot the minute Steam creates itAppsEvery app on Steam, newest change firstPackagesSubs and bundles, and what each containsDepotsDepots, manifests and install sizesTagsSteam's user tags and the games under themDevelopers & publishersCompanies and their cataloguesTechnologiesEngines, SDKs and anti-cheat found in the filesChange historyEvery PICS changelist as it lands
SignalsNineteen readings of the whole catalogueCompareAny games side by sideRecordsAll-time peaks and the days they were setReportsWeekly and monthly write-upsAlerts & newsroomWatch a game, get told when it movesSteam statusIs Steam up, right nowWeb API explorerTry the endpoints in the browser
EventsSteam's own events and the sales calendarCalculatorWhat a Steam account is worth, and its pile of shame
/
Sign in
/
SteamPeaks
The ultimate resource for Steam data.
ExploreChartsSalesSales and festsUpcomingPatchesEventsRecordsTrendingSignals
DatabaseAppsPackagesDepotsTagsDevelopersTechnologiesChange history
ToolsCalculatorCompareSearchAlertsSteam statusAPI
SiteMethodologyFAQDiscordSupportSign in via Steam
Not affiliated with Valve or Steam. Game names and artwork belong to their owners. All times UTC.
PrivacyCookiesFair useStatus
Boss KeyEventsSeptember 12, 2026 Update
In-game event

September 12, 2026 Update

Boss Key · published 12 Sep 2026, 03:42 UTC · build 25266256

All eventsPlayers around this dateRead on Steam

I. Adding Custom AI Tools

Custom AI tools are now supported.

You can configure them via the top menu bar → Help → AI.

  • Checked – Enabled
  • Unchecked – Disabled

Currently, several example tools are provided, and you can also extend them in specific ways (see Chapter 4 for details).


II. Custom System Prompt

You can now customize the system prompt.In the AI panel, click the More (three dots in the top-right corner) button → System Prompt to edit it.The prompt added here will be automatically sent to the AI on every call.You can add restrictions and requirements for the AI here.


III. Exporting the Work Audit Log

In the tool interface, click the More (three dots in the top-right corner) button → Export Audit Log to export the AI's work audit log.You can find it in the save directory.By reading this log, you can learn how the AI tools have been used.


IV. How to Extend Tools

1 What Is a Tool

1.1 Purpose

A Tool is a bridge for interaction between the AI and the game world. It is a C# method that can be called by the AI. The AI reads the tool's description and parameters, determines in which scenarios to call it, and continues reasoning based on the returned result.

1.2 Workflow

Player input → AI Agent analyzes intent → Decides to call a Tool → Tool executes specific logic → Returns result to AI → AI generates a reply based on the result or continues calling tools

1.3 Example

When the player says "Push that red box into the corner," the AI's workflow is:AI analysis: Need to find the red box first → Call find_object("red box")Result obtained: The box is at (5, 0, 3) → Call move_object("box", target position)After completion: Reply to the player, "I have pushed the red box into the corner for you."

2 Quick Start: Create Your First Tool

2.1 Create a Script File

Create a new C# script in the Assets/YourProjectName/Scripts/Tools/ directory, for example SayHelloTool.cs.

2.2 Minimal Working Template

csharp

using System.Threading;using System.Threading.Tasks;using Newtonsoft.Json.Linq;using UnAI.Tools; namespace RuntimeEditor.Tools.Examples{ public class SayHelloTool : ToolBase { // ========== Basic Metadata ========== public override string Id => "say_hello"; public override string DisplayName => "Say Hello"; public override string Description => "Greets the player and returns a greeting message."; public override ToolCategory Category => ToolCategory.General; public override ToolRiskLevel RiskLevel => ToolRiskLevel.Low; public override bool DefaultEnabled => true;  // ========== Parameter Definition ========== public override UnaiToolDefinition Definition => new() { Name = Id, Description = Description, ParametersSchema = JObject.Parse(@" { ""type"": ""object"", ""properties"": { ""playerName"": { ""type"": ""string"", ""description"": ""The player's name"" } }, ""required"": [""playerName""] }") };  // ========== Execution Logic ========== public override Task<UnaiToolResult> ExecuteAsync(UnaiToolCall call, CancellationToken ct) { var args = call.GetArguments(); string playerName = args["playerName"]?.ToString() ?? "Adventurer";  return Task.FromResult(new UnaiToolResult { Content = $"Hello, {playerName}! Welcome to this world." }); } }}

2.3 Automatic Registration

No additional action is required. ToolManager will automatically scan and discover all classes inheriting from ToolBase when the game starts.

2.4 Feature Verification

  1. Run the game → Open the tool management panel

  2. Find the "Say Hello" tool → Confirm it is enabled

  3. Talk to the AI: "Say hello to me" → The AI will call this tool and return a greeting

3 ToolBase Base Class Detailed Explanation

All custom tools must inherit from ToolBase and implement the following members.

3.1 Required Properties

Table 1 Required Properties of ToolBase

Property

Type

Description

Id

string

Unique tool identifier (used to save state and audit logs). Must be globally unique. Snake_case naming is recommended, such as get_player_status

DisplayName

string

Tool display name (shown in UI), such as "Get Player Status"

Description

string

Tool description (used by UI + AI). This is the key for the AI to determine when to call the tool, so it should be clear and specific

Category

ToolCategory

Tool category (see Section 7)

Definition

UnaiToolDefinition

Tool schema definition (see Section 4)

ExecuteAsync

Task

Tool execution logic (see Section 5)

3.2 Optional Virtual Properties

Table 2 Optional Virtual Properties of ToolBase

Property

Default Value

Description

RiskLevel

Low

Risk level (see Section 7)

DefaultEnabled

true

Whether enabled by default

RequiresConfirmation

RiskLevel >= High

Whether player confirmation is required

SupportsUndo

false

Whether undo is supported

TimeoutSeconds

30

Execution timeout (seconds); 0 means no limit

DependsOn

[]

Prerequisite tool IDs

RecommendedNext

[]

Recommended follow-up tool IDs

4 Parameter Definition (ParametersSchema)

ParametersSchema uses JSON Schema format to describe the tool's parameters. The AI will generate the correct call parameters based on this schema.

4.1 Basic Types

csharp

ParametersSchema = JObject.Parse(@"{ ""type"": ""object"", ""properties"": { ""name"": { ""type"": ""string"", ""description"": ""Name"" }, ""count"": { ""type"": ""integer"", ""description"": ""Count"" }, ""price"": { ""type"": ""number"", ""description"": ""Price"" }, ""enabled"": { ""type"": ""boolean"", ""description"": ""Whether enabled"" } }, ""required"": [""name""]}")

4.2 Array Type

csharp

ParametersSchema = JObject.Parse(@"{ ""type"": ""object"", ""properties"": { ""tags"": { ""type"": ""array"", ""items"": { ""type"": ""string"" }, ""description"": ""List of tags"" } }}")

4.3 Enum Values

csharp

ParametersSchema = JObject.Parse(@"{ ""type"": ""object"", ""properties"": { ""direction"": { ""type"": ""string"", ""enum"": [""north"", ""south"", ""east"", ""west""], ""description"": ""Movement direction"" } }}")

4.4 Nested Objects

csharp

ParametersSchema = JObject.Parse(@"{ ""type"": ""object"", ""properties"": { ""position"": { ""type"": ""object"", ""properties"": { ""x"": { ""type"": ""number"" }, ""y"": { ""type"": ""number"" }, ""z"": { ""type"": ""number"" } }, ""required"": [""x"", ""y"", ""z""], ""description"": ""Target coordinates"" } }}")

4.5 Description Writing Suggestions

The description should be as clear as documentation written for a new colleague. The AI relies entirely on it to determine the meaning of parameters.

Bad description:

json

"time" : { "description" : "Time" }

Good description:

json

"time": { "type": "integer", "description": "Waiting time in seconds. For example, 5 means waiting for 5 seconds. Value range: 1-60."}

5 Execution Logic (ExecuteAsync)

ExecuteAsync is the core of the tool. Parameters are obtained through call.GetArguments(), and it returns an UnaiToolResult.

5.1 Basic Template

csharp

public override async Task<UnaiToolResult> ExecuteAsync(UnaiToolCall call, CancellationToken ct){ // 1. Parse parameters var args = call.GetArguments(); string name = args["name"]?.ToString(); int count = Convert.ToInt32(args["count"]);  // 2. Validate parameters if (string.IsNullOrEmpty(name)) return new UnaiToolResult { Content = "Error: name parameter cannot be empty" };  // 3. Execute logic try { // Write the specific business logic here string result = DoSomething(name, count);  // 4. Return result return new UnaiToolResult { Content = $"Execution successful: {result}" }; } catch (Exception ex) { return new UnaiToolResult { Content = $"Execution failed: {ex.Message}" }; }}

5.2 Asynchronous Operations

For time-consuming operations (network requests, file reads/writes), use await:

csharp

public override async Task<UnaiToolResult> ExecuteAsync(UnaiToolCall call, CancellationToken ct){ // Use UnityWebRequest for an asynchronous request using (var request = UnityWebRequest.Get("https://example.com")) { var operation = request.SendWebRequest(); while (!operation.isDone) { if (ct.IsCancellationRequested) { request.Abort(); return new UnaiToolResult { Content = "Operation canceled" }; } await Task.Yield(); }  if (request.result != UnityWebRequest.Result.Success) return new UnaiToolResult { Content = $"Request failed: {request.error}" };  return new UnaiToolResult { Content = request.downloadHandler.text }; }}

5.3 Return Result Specification

Successful return:

csharp

return new UnaiToolResult { Content = "Operation completed successfully. The result is..." };

Failed return (describe the error in natural language):

csharp

return new UnaiToolResult { Content = "Error: Target object not found" };

Tip: The content of Content will be fed back to the AI as the result, and the AI will continue reasoning based on it. Therefore, even on failure, provide a clear error description so the AI has a chance to adjust its strategy.

6 Advanced Features

6.1 Progress Reporting

For long-running tools, you can use ReportProgress to report progress to the UI:

csharp

public override async Task<UnaiToolResult> ExecuteAsync(UnaiToolCall call, CancellationToken ct){ ReportProgress(0.1f, "Preparing..."); await SomeStep1();  ReportProgress(0.5f, "Processing..."); await SomeStep2();  ReportProgress(1f, "Completed"); return new UnaiToolResult { Content = "Operation completed" };}

6.2 Undo Support

If the tool supports undo (such as modifying files or moving objects), override SupportsUndo and UndoAsync:

csharp

public override bool SupportsUndo => true; private string _originalContent; public override async Task<UnaiToolResult> ExecuteAsync(UnaiToolCall call, CancellationToken ct){ // Save the original state _originalContent = File.ReadAllText(_filePath); // Modify the file File.WriteAllText(_filePath, "New content"); return new UnaiToolResult { Content = "File modified" };} public override Task<bool> UndoAsync(){ if (!string.IsNullOrEmpty(_originalContent)) { File.WriteAllText(_filePath, _originalContent); return Task.FromResult(true); } return Task.FromResult(false);}

6.3 Dependency Declaration

If tools have a calling order, you can declare dependencies:

csharp

// Before this tool executes, it is recommended to execute read_script firstpublic override string[] DependsOn => new[] { "read_script" }; // After this tool executes, it is recommended to call compile_projectpublic override string[] RecommendedNext => new[] { "compile_project" };

7 Tool Categories and Risk Levels

7.1 Tool Categories (ToolCategory)

Table 3 Tool Categories (ToolCategory)

Category

Description

Typical Tools

General

General

Greeting, time query

ProjectRead

Project reading

Read scripts, search files

CodeGeneration

Code generation

Create scripts, generate Prefabs

CodeEditing

Code editing

Modify scripts, rename

Compilation

Compilation verification

Compile project, get errors

SceneOperation

Scene operation

Create objects, move objects

AssetOperation

Asset operation

Load materials, manage assets

Debugging

Debugging and diagnostics

Read logs, web search

System

System-level

Shutdown, restart

7.2 Risk Levels (ToolRiskLevel)

Table 4 Risk Levels (ToolRiskLevel)

Level

Description

Confirmation Required by Default

Typical Tools

Low

Read-only operation, no side effects

false

Read, query, search

Medium

Modifies but reversible

false

Move, create, write files

High

Modifies and difficult to recover

true

Delete, overwrite, batch operations

Critical

Irreversible operation

true

Format, system-level operations

7.3 Configuration Suggestions

csharp

// Read-only tool: low risk, enabled by defaultpublic override ToolCategory Category => ToolCategory.ProjectRead;public override ToolRiskLevel RiskLevel => ToolRiskLevel.Low;public override bool DefaultEnabled => true; // Modifying tool: medium risk, enabled by default but loggedpublic override ToolCategory Category => ToolCategory.SceneOperation;public override ToolRiskLevel RiskLevel => ToolRiskLevel.Medium; // Dangerous tool: high risk, disabled by defaultpublic override ToolCategory Category => ToolCategory.CodeEditing;public override ToolRiskLevel RiskLevel => ToolRiskLevel.High;public override bool DefaultEnabled => false;

8 Complete Example Set

8.1 Query-Type Tool (No Parameters)

csharp

public class GetTimeTool : ToolBase{ public override string Id => "get_time"; public override string DisplayName => "Get Current Time"; public override string Description => "Returns the current time in the game world (hours and minutes)."; public override ToolCategory Category => ToolCategory.General; public override ToolRiskLevel RiskLevel => ToolRiskLevel.Low;  public override UnaiToolDefinition Definition => new() { Name = Id, Description = Description, ParametersSchema = JObject.Parse(@"{ ""type"": ""object"", ""properties"": {} }") };  public override Task<UnaiToolResult> ExecuteAsync(UnaiToolCall call, CancellationToken ct) { string time = DateTime.Now.ToString("HH:mm"); return Task.FromResult(new UnaiToolResult { Content = $"The current time is {time}" }); }}

8.2 Action-Type Tool (With Parameters)

csharp

public class SpawnEnemyTool : ToolBase{ public override string Id => "spawn_enemy"; public override string DisplayName => "Spawn Enemy"; public override string Description => "Spawns an enemy at the specified coordinates."; public override ToolCategory Category => ToolCategory.SceneOperation; public override ToolRiskLevel RiskLevel => ToolRiskLevel.Medium; public override bool DefaultEnabled => false;  public override UnaiToolDefinition Definition => new() { Name = Id, Description = Description, ParametersSchema = JObject.Parse(@"{ ""type"": ""object"", ""properties"": { ""enemyType"": { ""type"": ""string"", ""enum"": [""goblin"", ""skeleton"", ""dragon""], ""description"": ""Enemy type"" }, ""x"": { ""type"": ""number"", ""description"": ""X coordinate"" }, ""y"": { ""type"": ""number"", ""description"": ""Y coordinate"" }, ""z"": { ""type"": ""number"", ""description"": ""Z coordinate"" } }, ""required"": [""enemyType"", ""x"", ""y"", ""z""] }") };  public override Task<UnaiToolResult> ExecuteAsync(UnaiToolCall call, CancellationToken ct) { var args = call.GetArguments(); string type = args["enemyType"]?.ToString(); float x = Convert.ToSingle(args["x"]); float y = Convert.ToSingle(args["y"]); float z = Convert.ToSingle(args["z"]);  // Actual enemy spawning logic // Instantiate(enemyPrefab, new Vector3(x, y, z), Quaternion.identity);  return Task.FromResult(new UnaiToolResult { Content = $"Spawned {type} at ({x}, {y}, {z})" }); }}

8.3 Dangerous Tool (Requires Confirmation)

csharp

public class DeleteObjectTool : ToolBase{ public override string Id => "delete_object"; public override string DisplayName => "Delete Object"; public override string Description => "Permanently deletes the specified game object in the scene. This operation is irreversible."; public override ToolCategory Category => ToolCategory.SceneOperation; public override ToolRiskLevel RiskLevel => ToolRiskLevel.High; public override bool DefaultEnabled => false; // RequiresConfirmation defaults to true (because RiskLevel >= High)  public override UnaiToolDefinition Definition => new() { Name = Id, Description = Description, ParametersSchema = JObject.Parse(@"{ ""type"": ""object"", ""properties"": { ""objectName"": { ""type"": ""string"", ""description"": ""Name of the object to delete"" } }, ""required"": [""objectName""] }") };  public override Task<UnaiToolResult> ExecuteAsync(UnaiToolCall call, CancellationToken ct) { var args = call.GetArguments(); string name = args["objectName"]?.ToString();  // GameObject obj = GameObject.Find(name); // if (obj != null) Destroy(obj);  return Task.FromResult(new UnaiToolResult { Content = $"Deleted object {name}" }); }}

9 Debugging and Troubleshooting

9.1 The Tool Does Not Appear in the Panel

Table 5 Troubleshooting Items for a Tool Not Appearing in the Panel

Item

Description

Whether it inherits ToolBase

Check the class declaration: public class XxxTool : ToolBase

Whether it has a parameterless constructor

It must have a public parameterless constructor (the default one is fine)

Whether there are compilation errors

Check whether there are red errors in the Console

Whether it is in the correct assembly

If using .asmdef, the tool class and ToolManager must be in the same assembly (or modify the discovery logic to scan all assemblies)

Check logs

Look for the log [ToolManager] Discovered tool: xxx

9.2 The Tool Is Called by the AI but Has No Effect

Table 6 Troubleshooting Items for a Tool Being Called but Having No Effect

Item

Description

Whether Content is empty

The return result is passed to the AI, so it must contain meaningful content

Whether parameter parsing is correct

Use Debug.Log(args.ToString()) to print the parameters and confirm

Whether an exception is thrown

Check the Console for exception logs

9.3 Adding Debug Logs

csharp

public override async Task<UnaiToolResult> ExecuteAsync(UnaiToolCall call, CancellationToken ct){ var args = call.GetArguments(); Debug.Log($"[MyTool] Received parameters: {args}");  // ... execution logic ...  Debug.Log($"[MyTool] Returned result: {result}"); return result;}

9.4 Audit Log

ToolManager automatically records every tool execution. You can view it by clicking "Export Log" in the tool management panel:

[2026-09-11 10:30:00] true Get Player Status Parameters: {} Result: Health 100, Position (10.0, 0.0, 5.0) Duration: 12ms

10 Best Practices

10.1 Naming Conventions

Table 7 Naming Conventions

Item

Convention

Example

Class name

PascalCase + Tool suffix

GetPlayerStatusTool

Id

snake_case

get_player_status

DisplayName

Short name

Get Player Status

Description

Complete sentence explaining the purpose

Gets the player's current health, position, and current area.

10.2 Description Writing Tips

Description is the core basis for the AI to determine whether to call the tool. Write it as if explaining this feature to a new colleague:

Bad description:

"Player status"

Good description:

"Gets the player's current health, position, and current area. Use this when you need to understand the player's status, determine whether it is safe, or plan a movement route."

10.3 Parameter Design Principles

• Use as few parameters as possible: Make it easier for the AI to generate correct parameters

• Use enums to limit ranges: Avoid invalid values generated by the AI

• Provide default values: Set reasonable defaults for optional parameters

• Make parameter descriptions detailed: Include units, ranges, and formats

10.4 Error Handling

Always wrap execution logic in try-catch and return meaningful error information:

csharp

try{ // Execution logic}catch (Exception ex){ // Not only log it, but also return it to the AI so the AI has a chance to adjust return new UnaiToolResult { Content = $"Operation failed: {ex.Message}. Please check whether the parameters are correct." };}

10.5 Security Principles

• Never trust parameters generated by the AI: They must be validated

• High-risk operations must set RiskLevel = High or above

• Do not execute arbitrary code in tools (such as eval or reflection calls to unauthorized methods)

• Restrict file operations to the project directory (such as Assets/)

10.6 Performance Considerations

• Avoid time-consuming synchronous operations in ExecuteAsync: Use await Task.Run() or asynchronous APIs

• Report progress for long operations: Use ReportProgress

• Set a reasonable TimeoutSeconds: Prevent the AI from getting stuck

Appendix A: Quick Reference Card

The following template can be copied and used directly. It covers all elements of tool definition, parameter declaration, and execution logic:

csharp

public class MyCustomTool : ToolBase{ // ========== Required ========== public override string Id => "my_custom_tool"; public override string DisplayName => "My Tool"; public override string Description => "Describe what this tool does. The more specific, the better."; public override ToolCategory Category => ToolCategory.General;  public override UnaiToolDefinition Definition => new() { Name = Id, Description = Description, ParametersSchema = JObject.Parse(@"{ ""type"": ""object"", ""properties"": { ""param1"": { ""type"": ""string"", ""description"": ""Parameter description"" } }, ""required"": [""param1""] }") };  public override async Task<UnaiToolResult> ExecuteAsync(UnaiToolCall call, CancellationToken ct) { var args = call.GetArguments(); string param1 = args["param1"]?.ToString();  if (string.IsNullOrEmpty(param1)) return new UnaiToolResult { Content = "Error: param1 cannot be empty" };  try { // Your logic return new UnaiToolResult { Content = "Execution result" }; } catch (Exception ex) { return new UnaiToolResult { Content = $"Error: {ex.Message}" }; } }  // ========== Optional Overrides ========== public override ToolRiskLevel RiskLevel => ToolRiskLevel.Low; public override bool DefaultEnabled => true; public override bool SupportsUndo => false; public override int TimeoutSeconds => 30; public override string[] DependsOn => new string[] { }; public override string[] RecommendedNext => new string[] { };}

More from Boss Key

Other announcements

All events
Boss KeyGame update10 Sep 2026September 10, 2026 UpdateFixed compatibility issues with existing mods.Major update9 Sep 2026Update – September 9, 2026I. New AI Assistant Feature A new AI Assistant has been added. You can access it via the top menu bar: Help → AI . The AI can: · Answer your questions · Help you operate the editor How the AI Works The AI consists of two components: · AI Smart Router – The central dispatch hub. It receives your messages, intelligently selects which AI service to use for replies, and operates the tools you provi…In-game event5 Sep 2026Update – September 5, 20261. Custom Snap Points You can now create custom snap points to which objects will automatically align. To set one up: · In the Data Panel, select the snap table and click Create Table; · Add at least one snap point type; · Drag the snap point onto the table – a small green dot will appear at your cursor position. Any snappable object entering the minimum snap radius will then automatically snap…