Developers
Workflow
Point the runtime at something that already exists in your place.
Any Instance with observable state and a way to act on it qualifies: a character rig, a vehicle chassis, a boat hull, a conveyor, or a plain controller script that owns a group of parts. The runtime does not require a particular hierarchy, tag scheme or base class.
Bind that model to a normalised observation and actuation surface.
The adapter is the only code that knows what the thing physically is. Studio can scaffold one from the model — inferring seats, motors, attachments and attributes — which you then narrow by hand. Adapters are reusable across every instance of that class.
State exactly what the agent is allowed to perceive.
Observation is an allowlist, not a snapshot of the world. Fields you do not declare are not present in the planner’s input, are not written to diagnostics, and cannot be sent to an external provider. Declaring narrowly is both a privacy control and a performance control.
Enumerate what this class of actor can be asked to attempt.
Each action declares its parameters, its preconditions and its cost against declared resources. The planner can only compose plans out of this set — there is no escape hatch that lets it call arbitrary code at decision time.
Bind a role to a capability set, a constraint set and a resource budget.
Roles come from domain packs and are the unit of authority. A role grants capabilities, forbids conditions, caps spend, and can require external clearance. Anything the role does not grant is unreachable regardless of what the planner would prefer.
Give the actor an objective in the runtime’s own vocabulary.
Goals are declared with a satisfaction predicate, an optional decomposition and abandonment conditions. Your existing systems submit them — a quest system, a dispatcher, a scheduler, or a player request that has already passed your own checks.
Every proposed action is checked before it can proceed.
Validation runs against the capability set, the constraint set, resource availability, ownership and live world preconditions. A rejection returns a typed reason and the evidence it was decided from, which is what makes failures debuggable instead of mysterious.
Approved actions settle resources, actuate, and commit on the server.
Resource movement is atomic: a plan that cannot pay for itself never partially executes. Actuation goes back through the adapter, and the resulting state is written server-side. Clients observe the outcome; they never produce it.
Assert on behaviour, not on frame timing.
Scenario tests construct a world, attach actors, submit goals and run a fixed number of ticks against a fixed seed. Assertions are made over the resulting trace — which actions were proposed, which were rejected and why, and what state the actor ended in.
Read the decision, the rejection and the spend — without leaking anything.
Diagnostics expose the active goal, the current plan, the last rejection with its reason, and resource spend against budget. They are redacted by the same allowlist that governs observation, so enabling diagnostics cannot widen what leaves the server.
SDK concepts
actor is a model plus an adapter plus a role. A role comes from a domain pack and carries capabilities, constraints and budgets. A goal is submitted against an actor and decomposes into actions, which spend resources.Attachment is where the three pieces meet. If the role references a capability the adapter does not declare, attachment fails at that moment rather than at the first decision.
local ServerScriptService = game:GetService("ServerScriptService")
local Runtime = require(ServerScriptService.BDGAgent)
-- A gantry crane and a courier attach the same way. Only the adapter differs.
local crane = Runtime.attach(workspace.Port.CraneA, {
adapter = "machine/gantry",
role = "port.crane_operator",
goals = { "port.clear_inbound_queue" },
})
local courier = Runtime.attach(workspace.NPCs.Courier07, {
adapter = "humanoid/biped",
role = "logistics.courier",
goals = { "logistics.deliver_manifest" },
})Packs are the reusable unit. They travel between places, compose with one another, and are the only place a capability name is minted.
return {
id = "domain/port-operations",
roles = {
["port.crane_operator"] = {
capabilities = { "machine.slew", "machine.hoist", "machine.grip", "cargo.transfer" },
constraints = {
max_payload_tonnes = 40,
forbid_when = { "storm_warning", "quay_closed" },
require_clearance = "port.deck_clear",
},
resources = {
power = { budget_per_minute = 240 },
},
},
},
goals = {
["port.clear_inbound_queue"] = {
satisfied_when = "inbound_queue.depth == 0",
decompose_to = { "cargo.select_next", "cargo.transfer", "cargo.stow" },
abandon_when = { "storm_warning" },
},
},
}Studio workflow
Testing and diagnostics
Assertions cover what was proposed, what was rejected and why, and the state the actor ended in — the three things that actually tell you whether behaviour is correct.
Scenario("crane refuses an overweight lift", function(world)
local crane = world:attach("machine/gantry", "port.crane_operator")
world:place("container", { tonnes = 52, at = "quay.slot_3" })
world:submitGoal(crane, "port.clear_inbound_queue")
local trace = world:run({ ticks = 40, seed = 1 })
expect(trace).never:toCommit("hoist")
expect(trace):toReject("hoist", "constraint.max_payload_tonnes")
expect(trace):toEndInState("holding")
end)Inspection is redacted by the same allowlist that governs observation. Turning diagnostics on cannot widen what leaves the server.
local report = Runtime.inspect(actor)
-- report.goal -> the active normalised goal
-- report.plan -> ordered declared actions
-- report.last_rejection -> { action, reason, at_tick }
-- report.resources -> spend against declared budgets
-- report.authority -> the capability set in force for this role
-- Diagnostics are redacted by the same rules as external evaluation:
-- no player identifiers, no raw account data, no unrelated world state.Generally available
The concepts, the adapter contract and the authority model are written up in full and are free to read. The SDK itself ships with a subscription.